001 /*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.ldap.sdk;
022
023
024
025 import java.io.Serializable;
026 import java.util.ArrayList;
027 import java.util.Arrays;
028 import java.util.Collection;
029 import java.util.HashSet;
030 import java.util.LinkedHashSet;
031 import java.util.List;
032 import java.util.TreeMap;
033
034 import com.unboundid.asn1.ASN1Boolean;
035 import com.unboundid.asn1.ASN1Buffer;
036 import com.unboundid.asn1.ASN1BufferSequence;
037 import com.unboundid.asn1.ASN1BufferSet;
038 import com.unboundid.asn1.ASN1Element;
039 import com.unboundid.asn1.ASN1Exception;
040 import com.unboundid.asn1.ASN1OctetString;
041 import com.unboundid.asn1.ASN1Sequence;
042 import com.unboundid.asn1.ASN1Set;
043 import com.unboundid.asn1.ASN1StreamReader;
044 import com.unboundid.asn1.ASN1StreamReaderSequence;
045 import com.unboundid.asn1.ASN1StreamReaderSet;
046 import com.unboundid.ldap.matchingrules.CaseIgnoreStringMatchingRule;
047 import com.unboundid.ldap.matchingrules.MatchingRule;
048 import com.unboundid.ldap.sdk.schema.Schema;
049 import com.unboundid.util.ByteStringBuffer;
050
051 import static com.unboundid.ldap.sdk.LDAPMessages.*;
052 import static com.unboundid.util.Debug.*;
053 import static com.unboundid.util.StaticUtils.*;
054 import static com.unboundid.util.Validator.*;
055
056
057
058 /**
059 * This class provides a data structure that represents an LDAP search filter.
060 * It provides methods for creating various types of filters, as well as parsing
061 * a filter from a string. See
062 * <A HREF="http://www.ietf.org/rfc/rfc4515.txt">RFC 4515</A> for more
063 * information about representing search filters as strings.
064 * <BR><BR>
065 * The following filter types are defined:
066 * <UL>
067 * <LI><B>AND</B> -- This is used to indicate that a filter should match an
068 * entry only if all of the embedded filter components match that entry.
069 * An AND filter with zero embedded filter components is considered an
070 * LDAP TRUE filter as defined in
071 * <A HREF="http://www.ietf.org/rfc/rfc4526.txt">RFC 4526</A> and will
072 * match any entry. AND filters contain only a set of embedded filter
073 * components, and each of those embedded components can itself be any
074 * type of filter, including an AND, OR, or NOT filter with additional
075 * embedded components.</LI>
076 * <LI><B>OR</B> -- This is used to indicate that a filter should match an
077 * entry only if at least one of the embedded filter components matches
078 * that entry. An OR filter with zero embedded filter components is
079 * considered an LDAP FALSE filter as defined in
080 * <A HREF="http://www.ietf.org/rfc/rfc4526.txt">RFC 4526</A> and will
081 * never match any entry. OR filters contain only a set of embedded
082 * filter components, and each of those embedded components can itself be
083 * any type of filter, including an AND, OR, or NOT filter with additional
084 * embedded components.</LI>
085 * <LI><B>NOT</B> -- This is used to indicate that a filter should match an
086 * entry only if the embedded NOT component does not match the entry. A
087 * NOT filter contains only a single embedded NOT filter component, but
088 * that embedded component can itself be any type of filter, including an
089 * AND, OR, or NOT filter with additional embedded components.</LI>
090 * <LI><B>EQUALITY</B> -- This is used to indicate that a filter should match
091 * an entry only if the entry contains a value for the specified attribute
092 * that is equal to the provided assertion value. An equality filter
093 * contains only an attribute name and an assertion value.</LI>
094 * <LI><B>SUBSTRING</B> -- This is used to indicate that a filter should match
095 * an entry only if the entry contains at least one value for the
096 * specified attribute that matches the provided substring assertion. The
097 * substring assertion must contain at least one element of the following
098 * types:
099 * <UL>
100 * <LI>subInitial -- This indicates that the specified string must
101 * appear at the beginning of the attribute value. There can be at
102 * most one subInitial element in a substring assertion.</LI>
103 * <LI>subAny -- This indicates that the specified string may appear
104 * anywhere in the attribute value. There can be any number of
105 * substring subAny elements in a substring assertion. If there are
106 * multiple subAny elements, then they must match in the order that
107 * they are provided.</LI>
108 * <LI>subFinal -- This indicates that the specified string must appear
109 * at the end of the attribute value. There can be at most one
110 * subFinal element in a substring assertion.</LI>
111 * </UL>
112 * A substring filter contains only an attribute name and subInitial,
113 * subAny, and subFinal elements.</LI>
114 * <LI><B>GREATER-OR-EQUAL</B> -- This is used to indicate that a filter
115 * should match an entry only if that entry contains at least one value
116 * for the specified attribute that is greater than or equal to the
117 * provided assertion value. A greater-or-equal filter contains only an
118 * attribute name and an assertion value.</LI>
119 * <LI><B>LESS-OR-EQUAL</B> -- This is used to indicate that a filter should
120 * match an entry only if that entry contains at least one value for the
121 * specified attribute that is less than or equal to the provided
122 * assertion value. A less-or-equal filter contains only an attribute
123 * name and an assertion value.</LI>
124 * <LI><B>PRESENCE</B> -- This is used to indicate that a filter should match
125 * an entry only if the entry contains at least one value for the
126 * specified attribute. A presence filter contains only an attribute
127 * name.</LI>
128 * <LI><B>APPROXIMATE-MATCH</B> -- This is used to indicate that a filter
129 * should match an entry only if the entry contains at least one value for
130 * the specified attribute that is approximately equal to the provided
131 * assertion value. The definition of "approximately equal to" may vary
132 * from one server to another, and from one attribute to another, but it
133 * is often implemented as a "sounds like" match using a variant of the
134 * metaphone or double-metaphone algorithm. An approximate-match filter
135 * contains only an attribute name and an assertion value.</LI>
136 * <LI><B>EXTENSIBLE-MATCH</B> -- This is used to perform advanced types of
137 * matching against entries, according to the following criteria:
138 * <UL>
139 * <LI>If an attribute name is provided, then the assertion value must
140 * match one of the values for that attribute (potentially including
141 * values contained in the entry's DN). If a matching rule ID is
142 * also provided, then the associated matching rule will be used to
143 * determine whether there is a match; otherwise the default
144 * equality matching rule for that attribute will be used.</LI>
145 * <LI>If no attribute name is provided, then a matching rule ID must be
146 * given, and the corresponding matching rule will be used to
147 * determine whether any attribute in the target entry (potentially
148 * including attributes contained in the entry's DN) has at least
149 * one value that matches the provided assertion value.</LI>
150 * <LI>If the dnAttributes flag is set, then attributes contained in the
151 * entry's DN will also be evaluated to determine if they match the
152 * filter criteria. If it is not set, then attributes contained in
153 * the entry's DN (other than those contained in its RDN which are
154 * also present as separate attributes in the entry) will not be
155 * examined.</LI>
156 * </UL>
157 * An extensible match filter contains only an attribute name, matching
158 * rule ID, dnAttributes flag, and an assertion value.</LI>
159 * </UL>
160 * <BR><BR>
161 * There are two primary ways to create a search filter. The first is to create
162 * a filter from its string representation with the
163 * {@code Filter#create(String)} method, using the syntax described in RFC 4515.
164 * For example:
165 * <PRE>
166 * Filter f1 = Filter.create("(objectClass=*)");
167 * Filter f2 = Filter.create("(uid=john.doe)");
168 * Filter f3 = Filter.create("(|(givenName=John)(givenName=Johnathan))");
169 * </PRE>
170 * <BR><BR>
171 * Creating a filter from its string representation is a common approach and
172 * seems to be relatively straightforward, but it does have some hidden dangers.
173 * This primarily comes from the potential for special characters in the filter
174 * string which need to be properly escaped. If this isn't done, then the
175 * search may fail or behave unexpectedly, or worse it could lead to a
176 * vulnerability in the application in which a malicious user could trick the
177 * application into retrieving more information than it should have. To avoid
178 * these problems, it may be better to construct filters from their individual
179 * components rather than their string representations, like:
180 * <PRE>
181 * Filter f1 = Filter.createPresenceFilter("objectClass");
182 * Filter f2 = Filter.createEqualityFilter("uid", "john.doe");
183 * Filter f3 = Filter.createORFilter(
184 * Filter.createEqualityFilter("givenName", "John"),
185 * Filter.createEqualityFilter("givenName", "Johnathan"));
186 * </PRE>
187 * In general, it is recommended to avoid creating filters from their string
188 * representations if any of that string representation may include
189 * user-provided data or special characters including non-ASCII characters,
190 * parentheses, asterisks, or backslashes.
191 */
192 public final class Filter
193 implements Serializable
194 {
195 /**
196 * The BER type for AND search filters.
197 */
198 public static final byte FILTER_TYPE_AND = (byte) 0xA0;
199
200
201
202 /**
203 * The BER type for OR search filters.
204 */
205 public static final byte FILTER_TYPE_OR = (byte) 0xA1;
206
207
208
209 /**
210 * The BER type for NOT search filters.
211 */
212 public static final byte FILTER_TYPE_NOT = (byte) 0xA2;
213
214
215
216 /**
217 * The BER type for equality search filters.
218 */
219 public static final byte FILTER_TYPE_EQUALITY = (byte) 0xA3;
220
221
222
223 /**
224 * The BER type for substring search filters.
225 */
226 public static final byte FILTER_TYPE_SUBSTRING = (byte) 0xA4;
227
228
229
230 /**
231 * The BER type for greaterOrEqual search filters.
232 */
233 public static final byte FILTER_TYPE_GREATER_OR_EQUAL = (byte) 0xA5;
234
235
236
237 /**
238 * The BER type for lessOrEqual search filters.
239 */
240 public static final byte FILTER_TYPE_LESS_OR_EQUAL = (byte) 0xA6;
241
242
243
244 /**
245 * The BER type for presence search filters.
246 */
247 public static final byte FILTER_TYPE_PRESENCE = (byte) 0x87;
248
249
250
251 /**
252 * The BER type for approximate match search filters.
253 */
254 public static final byte FILTER_TYPE_APPROXIMATE_MATCH = (byte) 0xA8;
255
256
257
258 /**
259 * The BER type for extensible match search filters.
260 */
261 public static final byte FILTER_TYPE_EXTENSIBLE_MATCH = (byte) 0xA9;
262
263
264
265 /**
266 * The BER type for the subInitial substring filter element.
267 */
268 private static final byte SUBSTRING_TYPE_SUBINITIAL = (byte) 0x80;
269
270
271
272 /**
273 * The BER type for the subAny substring filter element.
274 */
275 private static final byte SUBSTRING_TYPE_SUBANY = (byte) 0x81;
276
277
278
279 /**
280 * The BER type for the subFinal substring filter element.
281 */
282 private static final byte SUBSTRING_TYPE_SUBFINAL = (byte) 0x82;
283
284
285
286 /**
287 * The BER type for the matching rule ID extensible match filter element.
288 */
289 private static final byte EXTENSIBLE_TYPE_MATCHING_RULE_ID = (byte) 0x81;
290
291
292
293 /**
294 * The BER type for the attribute name extensible match filter element.
295 */
296 private static final byte EXTENSIBLE_TYPE_ATTRIBUTE_NAME = (byte) 0x82;
297
298
299
300 /**
301 * The BER type for the match value extensible match filter element.
302 */
303 private static final byte EXTENSIBLE_TYPE_MATCH_VALUE = (byte) 0x83;
304
305
306
307 /**
308 * The BER type for the DN attributes extensible match filter element.
309 */
310 private static final byte EXTENSIBLE_TYPE_DN_ATTRIBUTES = (byte) 0x84;
311
312
313
314 /**
315 * The set of filters that will be used if there are no subordinate filters.
316 */
317 private static final Filter[] NO_FILTERS = new Filter[0];
318
319
320
321 /**
322 * The set of subAny components that will be used if there are no subAny
323 * components.
324 */
325 private static final ASN1OctetString[] NO_SUB_ANY = new ASN1OctetString[0];
326
327
328
329 /**
330 * The serial version UID for this serializable class.
331 */
332 private static final long serialVersionUID = -2734184402804691970L;
333
334
335
336 // The assertion value for this filter.
337 private final ASN1OctetString assertionValue;
338
339 // The subFinal component for this filter.
340 private final ASN1OctetString subFinal;
341
342 // The subInitial component for this filter.
343 private final ASN1OctetString subInitial;
344
345 // The subAny components for this filter.
346 private final ASN1OctetString[] subAny;
347
348 // The dnAttrs element for this filter.
349 private final boolean dnAttributes;
350
351 // The filter component to include in a NOT filter.
352 private final Filter notComp;
353
354 // The set of filter components to include in an AND or OR filter.
355 private final Filter[] filterComps;
356
357 // The filter type for this search filter.
358 private final byte filterType;
359
360 // The attribute name for this filter.
361 private final String attrName;
362
363 // The string representation of this search filter.
364 private volatile String filterString;
365
366 // The matching rule ID for this filter.
367 private final String matchingRuleID;
368
369 // The normalized string representation of this search filter.
370 private volatile String normalizedString;
371
372
373
374 /**
375 * Creates a new filter with the appropriate subset of the provided
376 * information.
377 *
378 * @param filterString The string representation of this search filter.
379 * It may be {@code null} if it is not yet known.
380 * @param filterType The filter type for this filter.
381 * @param filterComps The set of filter components for this filter.
382 * @param notComp The filter component for this NOT filter.
383 * @param attrName The name of the target attribute for this filter.
384 * @param assertionValue Then assertion value for this filter.
385 * @param subInitial The subInitial component for this filter.
386 * @param subAny The set of subAny components for this filter.
387 * @param subFinal The subFinal component for this filter.
388 * @param matchingRuleID The matching rule ID for this filter.
389 * @param dnAttributes The dnAttributes flag.
390 */
391 private Filter(final String filterString, final byte filterType,
392 final Filter[] filterComps, final Filter notComp,
393 final String attrName, final ASN1OctetString assertionValue,
394 final ASN1OctetString subInitial,
395 final ASN1OctetString[] subAny, final ASN1OctetString subFinal,
396 final String matchingRuleID, final boolean dnAttributes)
397 {
398 this.filterString = filterString;
399 this.filterType = filterType;
400 this.filterComps = filterComps;
401 this.notComp = notComp;
402 this.attrName = attrName;
403 this.assertionValue = assertionValue;
404 this.subInitial = subInitial;
405 this.subAny = subAny;
406 this.subFinal = subFinal;
407 this.matchingRuleID = matchingRuleID;
408 this.dnAttributes = dnAttributes;
409 }
410
411
412
413 /**
414 * Creates a new AND search filter with the provided components.
415 *
416 * @param andComponents The set of filter components to include in the AND
417 * filter. It must not be {@code null}.
418 *
419 * @return The created AND search filter.
420 */
421 public static Filter createANDFilter(final Filter... andComponents)
422 {
423 ensureNotNull(andComponents);
424
425 return new Filter(null, FILTER_TYPE_AND, andComponents, null, null, null,
426 null, NO_SUB_ANY, null, null, false);
427 }
428
429
430
431 /**
432 * Creates a new AND search filter with the provided components.
433 *
434 * @param andComponents The set of filter components to include in the AND
435 * filter. It must not be {@code null}.
436 *
437 * @return The created AND search filter.
438 */
439 public static Filter createANDFilter(final List<Filter> andComponents)
440 {
441 ensureNotNull(andComponents);
442
443 return new Filter(null, FILTER_TYPE_AND,
444 andComponents.toArray(new Filter[andComponents.size()]),
445 null, null, null, null, NO_SUB_ANY, null, null, false);
446 }
447
448
449
450 /**
451 * Creates a new AND search filter with the provided components.
452 *
453 * @param andComponents The set of filter components to include in the AND
454 * filter. It must not be {@code null}.
455 *
456 * @return The created AND search filter.
457 */
458 public static Filter createANDFilter(final Collection<Filter> andComponents)
459 {
460 ensureNotNull(andComponents);
461
462 return new Filter(null, FILTER_TYPE_AND,
463 andComponents.toArray(new Filter[andComponents.size()]),
464 null, null, null, null, NO_SUB_ANY, null, null, false);
465 }
466
467
468
469 /**
470 * Creates a new OR search filter with the provided components.
471 *
472 * @param orComponents The set of filter components to include in the OR
473 * filter. It must not be {@code null}.
474 *
475 * @return The created OR search filter.
476 */
477 public static Filter createORFilter(final Filter... orComponents)
478 {
479 ensureNotNull(orComponents);
480
481 return new Filter(null, FILTER_TYPE_OR, orComponents, null, null, null,
482 null, NO_SUB_ANY, null, null, false);
483 }
484
485
486
487 /**
488 * Creates a new OR search filter with the provided components.
489 *
490 * @param orComponents The set of filter components to include in the OR
491 * filter. It must not be {@code null}.
492 *
493 * @return The created OR search filter.
494 */
495 public static Filter createORFilter(final List<Filter> orComponents)
496 {
497 ensureNotNull(orComponents);
498
499 return new Filter(null, FILTER_TYPE_OR,
500 orComponents.toArray(new Filter[orComponents.size()]),
501 null, null, null, null, NO_SUB_ANY, null, null, false);
502 }
503
504
505
506 /**
507 * Creates a new OR search filter with the provided components.
508 *
509 * @param orComponents The set of filter components to include in the OR
510 * filter. It must not be {@code null}.
511 *
512 * @return The created OR search filter.
513 */
514 public static Filter createORFilter(final Collection<Filter> orComponents)
515 {
516 ensureNotNull(orComponents);
517
518 return new Filter(null, FILTER_TYPE_OR,
519 orComponents.toArray(new Filter[orComponents.size()]),
520 null, null, null, null, NO_SUB_ANY, null, null, false);
521 }
522
523
524
525 /**
526 * Creates a new NOT search filter with the provided component.
527 *
528 * @param notComponent The filter component to include in this NOT filter.
529 * It must not be {@code null}.
530 *
531 * @return The created NOT search filter.
532 */
533 public static Filter createNOTFilter(final Filter notComponent)
534 {
535 ensureNotNull(notComponent);
536
537 return new Filter(null, FILTER_TYPE_NOT, NO_FILTERS, notComponent, null,
538 null, null, NO_SUB_ANY, null, null, false);
539 }
540
541
542
543 /**
544 * Creates a new equality search filter with the provided information.
545 *
546 * @param attributeName The attribute name for this equality filter. It
547 * must not be {@code null}.
548 * @param assertionValue The assertion value for this equality filter. It
549 * must not be {@code null}.
550 *
551 * @return The created equality search filter.
552 */
553 public static Filter createEqualityFilter(final String attributeName,
554 final String assertionValue)
555 {
556 ensureNotNull(attributeName, assertionValue);
557
558 return new Filter(null, FILTER_TYPE_EQUALITY, NO_FILTERS, null,
559 attributeName, new ASN1OctetString(assertionValue), null,
560 NO_SUB_ANY, null, null, false);
561 }
562
563
564
565 /**
566 * Creates a new equality search filter with the provided information.
567 *
568 * @param attributeName The attribute name for this equality filter. It
569 * must not be {@code null}.
570 * @param assertionValue The assertion value for this equality filter. It
571 * must not be {@code null}.
572 *
573 * @return The created equality search filter.
574 */
575 public static Filter createEqualityFilter(final String attributeName,
576 final byte[] assertionValue)
577 {
578 ensureNotNull(attributeName, assertionValue);
579
580 return new Filter(null, FILTER_TYPE_EQUALITY, NO_FILTERS, null,
581 attributeName, new ASN1OctetString(assertionValue), null,
582 NO_SUB_ANY, null, null, false);
583 }
584
585
586
587 /**
588 * Creates a new equality search filter with the provided information.
589 *
590 * @param attributeName The attribute name for this equality filter. It
591 * must not be {@code null}.
592 * @param assertionValue The assertion value for this equality filter. It
593 * must not be {@code null}.
594 *
595 * @return The created equality search filter.
596 */
597 static Filter createEqualityFilter(final String attributeName,
598 final ASN1OctetString assertionValue)
599 {
600 ensureNotNull(attributeName, assertionValue);
601
602 return new Filter(null, FILTER_TYPE_EQUALITY, NO_FILTERS, null,
603 attributeName, assertionValue, null, NO_SUB_ANY, null,
604 null, false);
605 }
606
607
608
609 /**
610 * Creates a new substring search filter with the provided information. At
611 * least one of the subInitial, subAny, and subFinal components must not be
612 * {@code null}.
613 *
614 * @param attributeName The attribute name for this substring filter. It
615 * must not be {@code null}.
616 * @param subInitial The subInitial component for this substring filter.
617 * @param subAny The set of subAny components for this substring
618 * filter.
619 * @param subFinal The subFinal component for this substring filter.
620 *
621 * @return The created substring search filter.
622 */
623 public static Filter createSubstringFilter(final String attributeName,
624 final String subInitial,
625 final String[] subAny,
626 final String subFinal)
627 {
628 ensureNotNull(attributeName);
629 ensureTrue((subInitial != null) ||
630 ((subAny != null) && (subAny.length > 0)) ||
631 (subFinal != null));
632
633 final ASN1OctetString subInitialOS;
634 if (subInitial == null)
635 {
636 subInitialOS = null;
637 }
638 else
639 {
640 subInitialOS = new ASN1OctetString(subInitial);
641 }
642
643 final ASN1OctetString[] subAnyArray;
644 if (subAny == null)
645 {
646 subAnyArray = NO_SUB_ANY;
647 }
648 else
649 {
650 subAnyArray = new ASN1OctetString[subAny.length];
651 for (int i=0; i < subAny.length; i++)
652 {
653 subAnyArray[i] = new ASN1OctetString(subAny[i]);
654 }
655 }
656
657 final ASN1OctetString subFinalOS;
658 if (subFinal == null)
659 {
660 subFinalOS = null;
661 }
662 else
663 {
664 subFinalOS = new ASN1OctetString(subFinal);
665 }
666
667 return new Filter(null, FILTER_TYPE_SUBSTRING, NO_FILTERS, null,
668 attributeName, null, subInitialOS, subAnyArray,
669 subFinalOS, null, false);
670 }
671
672
673
674 /**
675 * Creates a new substring search filter with the provided information. At
676 * least one of the subInitial, subAny, and subFinal components must not be
677 * {@code null}.
678 *
679 * @param attributeName The attribute name for this substring filter. It
680 * must not be {@code null}.
681 * @param subInitial The subInitial component for this substring filter.
682 * @param subAny The set of subAny components for this substring
683 * filter.
684 * @param subFinal The subFinal component for this substring filter.
685 *
686 * @return The created substring search filter.
687 */
688 public static Filter createSubstringFilter(final String attributeName,
689 final byte[] subInitial,
690 final byte[][] subAny,
691 final byte[] subFinal)
692 {
693 ensureNotNull(attributeName);
694 ensureTrue((subInitial != null) ||
695 ((subAny != null) && (subAny.length > 0)) ||
696 (subFinal != null));
697
698 final ASN1OctetString subInitialOS;
699 if (subInitial == null)
700 {
701 subInitialOS = null;
702 }
703 else
704 {
705 subInitialOS = new ASN1OctetString(subInitial);
706 }
707
708 final ASN1OctetString[] subAnyArray;
709 if (subAny == null)
710 {
711 subAnyArray = NO_SUB_ANY;
712 }
713 else
714 {
715 subAnyArray = new ASN1OctetString[subAny.length];
716 for (int i=0; i < subAny.length; i++)
717 {
718 subAnyArray[i] = new ASN1OctetString(subAny[i]);
719 }
720 }
721
722 final ASN1OctetString subFinalOS;
723 if (subFinal == null)
724 {
725 subFinalOS = null;
726 }
727 else
728 {
729 subFinalOS = new ASN1OctetString(subFinal);
730 }
731
732 return new Filter(null, FILTER_TYPE_SUBSTRING, NO_FILTERS, null,
733 attributeName, null, subInitialOS, subAnyArray,
734 subFinalOS, null, false);
735 }
736
737
738
739 /**
740 * Creates a new substring search filter with the provided information. At
741 * least one of the subInitial, subAny, and subFinal components must not be
742 * {@code null}.
743 *
744 * @param attributeName The attribute name for this substring filter. It
745 * must not be {@code null}.
746 * @param subInitial The subInitial component for this substring filter.
747 * @param subAny The set of subAny components for this substring
748 * filter.
749 * @param subFinal The subFinal component for this substring filter.
750 *
751 * @return The created substring search filter.
752 */
753 static Filter createSubstringFilter(final String attributeName,
754 final ASN1OctetString subInitial,
755 final ASN1OctetString[] subAny,
756 final ASN1OctetString subFinal)
757 {
758 ensureNotNull(attributeName);
759 ensureTrue((subInitial != null) ||
760 ((subAny != null) && (subAny.length > 0)) ||
761 (subFinal != null));
762
763 if (subAny == null)
764 {
765 return new Filter(null, FILTER_TYPE_SUBSTRING, NO_FILTERS, null,
766 attributeName, null, subInitial, NO_SUB_ANY, subFinal,
767 null, false);
768 }
769 else
770 {
771 return new Filter(null, FILTER_TYPE_SUBSTRING, NO_FILTERS, null,
772 attributeName, null, subInitial, subAny, subFinal, null,
773 false);
774 }
775 }
776
777
778
779 /**
780 * Creates a new greater-or-equal search filter with the provided information.
781 *
782 * @param attributeName The attribute name for this greater-or-equal
783 * filter. It must not be {@code null}.
784 * @param assertionValue The assertion value for this greater-or-equal
785 * filter. It must not be {@code null}.
786 *
787 * @return The created greater-or-equal search filter.
788 */
789 public static Filter createGreaterOrEqualFilter(final String attributeName,
790 final String assertionValue)
791 {
792 ensureNotNull(attributeName, assertionValue);
793
794 return new Filter(null, FILTER_TYPE_GREATER_OR_EQUAL, NO_FILTERS, null,
795 attributeName, new ASN1OctetString(assertionValue), null,
796 NO_SUB_ANY, null, null, false);
797 }
798
799
800
801 /**
802 * Creates a new greater-or-equal search filter with the provided information.
803 *
804 * @param attributeName The attribute name for this greater-or-equal
805 * filter. It must not be {@code null}.
806 * @param assertionValue The assertion value for this greater-or-equal
807 * filter. It must not be {@code null}.
808 *
809 * @return The created greater-or-equal search filter.
810 */
811 public static Filter createGreaterOrEqualFilter(final String attributeName,
812 final byte[] assertionValue)
813 {
814 ensureNotNull(attributeName, assertionValue);
815
816 return new Filter(null, FILTER_TYPE_GREATER_OR_EQUAL, NO_FILTERS, null,
817 attributeName, new ASN1OctetString(assertionValue), null,
818 NO_SUB_ANY, null, null, false);
819 }
820
821
822
823 /**
824 * Creates a new greater-or-equal search filter with the provided information.
825 *
826 * @param attributeName The attribute name for this greater-or-equal
827 * filter. It must not be {@code null}.
828 * @param assertionValue The assertion value for this greater-or-equal
829 * filter. It must not be {@code null}.
830 *
831 * @return The created greater-or-equal search filter.
832 */
833 static Filter createGreaterOrEqualFilter(final String attributeName,
834 final ASN1OctetString assertionValue)
835 {
836 ensureNotNull(attributeName, assertionValue);
837
838 return new Filter(null, FILTER_TYPE_GREATER_OR_EQUAL, NO_FILTERS, null,
839 attributeName, assertionValue, null, NO_SUB_ANY, null,
840 null, false);
841 }
842
843
844
845 /**
846 * Creates a new less-or-equal search filter with the provided information.
847 *
848 * @param attributeName The attribute name for this less-or-equal
849 * filter. It must not be {@code null}.
850 * @param assertionValue The assertion value for this less-or-equal
851 * filter. It must not be {@code null}.
852 *
853 * @return The created less-or-equal search filter.
854 */
855 public static Filter createLessOrEqualFilter(final String attributeName,
856 final String assertionValue)
857 {
858 ensureNotNull(attributeName, assertionValue);
859
860 return new Filter(null, FILTER_TYPE_LESS_OR_EQUAL, NO_FILTERS, null,
861 attributeName, new ASN1OctetString(assertionValue), null,
862 NO_SUB_ANY, null, null, false);
863 }
864
865
866
867 /**
868 * Creates a new less-or-equal search filter with the provided information.
869 *
870 * @param attributeName The attribute name for this less-or-equal
871 * filter. It must not be {@code null}.
872 * @param assertionValue The assertion value for this less-or-equal
873 * filter. It must not be {@code null}.
874 *
875 * @return The created less-or-equal search filter.
876 */
877 public static Filter createLessOrEqualFilter(final String attributeName,
878 final byte[] assertionValue)
879 {
880 ensureNotNull(attributeName, assertionValue);
881
882 return new Filter(null, FILTER_TYPE_LESS_OR_EQUAL, NO_FILTERS, null,
883 attributeName, new ASN1OctetString(assertionValue), null,
884 NO_SUB_ANY, null, null, false);
885 }
886
887
888
889 /**
890 * Creates a new less-or-equal search filter with the provided information.
891 *
892 * @param attributeName The attribute name for this less-or-equal
893 * filter. It must not be {@code null}.
894 * @param assertionValue The assertion value for this less-or-equal
895 * filter. It must not be {@code null}.
896 *
897 * @return The created less-or-equal search filter.
898 */
899 static Filter createLessOrEqualFilter(final String attributeName,
900 final ASN1OctetString assertionValue)
901 {
902 ensureNotNull(attributeName, assertionValue);
903
904 return new Filter(null, FILTER_TYPE_LESS_OR_EQUAL, NO_FILTERS, null,
905 attributeName, assertionValue, null, NO_SUB_ANY, null,
906 null, false);
907 }
908
909
910
911 /**
912 * Creates a new presence search filter with the provided information.
913 *
914 * @param attributeName The attribute name for this presence filter. It
915 * must not be {@code null}.
916 *
917 * @return The created presence search filter.
918 */
919 public static Filter createPresenceFilter(final String attributeName)
920 {
921 ensureNotNull(attributeName);
922
923 return new Filter(null, FILTER_TYPE_PRESENCE, NO_FILTERS, null,
924 attributeName, null, null, NO_SUB_ANY, null, null, false);
925 }
926
927
928
929 /**
930 * Creates a new approximate match search filter with the provided
931 * information.
932 *
933 * @param attributeName The attribute name for this approximate match
934 * filter. It must not be {@code null}.
935 * @param assertionValue The assertion value for this approximate match
936 * filter. It must not be {@code null}.
937 *
938 * @return The created approximate match search filter.
939 */
940 public static Filter createApproximateMatchFilter(final String attributeName,
941 final String assertionValue)
942 {
943 ensureNotNull(attributeName, assertionValue);
944
945 return new Filter(null, FILTER_TYPE_APPROXIMATE_MATCH, NO_FILTERS, null,
946 attributeName, new ASN1OctetString(assertionValue), null,
947 NO_SUB_ANY, null, null, false);
948 }
949
950
951
952 /**
953 * Creates a new approximate match search filter with the provided
954 * information.
955 *
956 * @param attributeName The attribute name for this approximate match
957 * filter. It must not be {@code null}.
958 * @param assertionValue The assertion value for this approximate match
959 * filter. It must not be {@code null}.
960 *
961 * @return The created approximate match search filter.
962 */
963 public static Filter createApproximateMatchFilter(final String attributeName,
964 final byte[] assertionValue)
965 {
966 ensureNotNull(attributeName, assertionValue);
967
968 return new Filter(null, FILTER_TYPE_APPROXIMATE_MATCH, NO_FILTERS, null,
969 attributeName, new ASN1OctetString(assertionValue), null,
970 NO_SUB_ANY, null, null, false);
971 }
972
973
974
975 /**
976 * Creates a new approximate match search filter with the provided
977 * information.
978 *
979 * @param attributeName The attribute name for this approximate match
980 * filter. It must not be {@code null}.
981 * @param assertionValue The assertion value for this approximate match
982 * filter. It must not be {@code null}.
983 *
984 * @return The created approximate match search filter.
985 */
986 static Filter createApproximateMatchFilter(final String attributeName,
987 final ASN1OctetString assertionValue)
988 {
989 ensureNotNull(attributeName, assertionValue);
990
991 return new Filter(null, FILTER_TYPE_APPROXIMATE_MATCH, NO_FILTERS, null,
992 attributeName, assertionValue, null, NO_SUB_ANY, null,
993 null, false);
994 }
995
996
997
998 /**
999 * Creates a new extensible match search filter with the provided
1000 * information. At least one of the attribute name and matching rule ID must
1001 * be specified, and the assertion value must always be present.
1002 *
1003 * @param attributeName The attribute name for this extensible match
1004 * filter.
1005 * @param matchingRuleID The matching rule ID for this extensible match
1006 * filter.
1007 * @param dnAttributes Indicates whether the match should be performed
1008 * against attributes in the target entry's DN.
1009 * @param assertionValue The assertion value for this extensible match
1010 * filter. It must not be {@code null}.
1011 *
1012 * @return The created extensible match search filter.
1013 */
1014 public static Filter createExtensibleMatchFilter(final String attributeName,
1015 final String matchingRuleID,
1016 final boolean dnAttributes,
1017 final String assertionValue)
1018 {
1019 ensureNotNull(assertionValue);
1020 ensureFalse((attributeName == null) && (matchingRuleID == null));
1021
1022 return new Filter(null, FILTER_TYPE_EXTENSIBLE_MATCH, NO_FILTERS, null,
1023 attributeName, new ASN1OctetString(assertionValue), null,
1024 NO_SUB_ANY, null, matchingRuleID, dnAttributes);
1025 }
1026
1027
1028
1029 /**
1030 * Creates a new extensible match search filter with the provided
1031 * information. At least one of the attribute name and matching rule ID must
1032 * be specified, and the assertion value must always be present.
1033 *
1034 * @param attributeName The attribute name for this extensible match
1035 * filter.
1036 * @param matchingRuleID The matching rule ID for this extensible match
1037 * filter.
1038 * @param dnAttributes Indicates whether the match should be performed
1039 * against attributes in the target entry's DN.
1040 * @param assertionValue The assertion value for this extensible match
1041 * filter. It must not be {@code null}.
1042 *
1043 * @return The created extensible match search filter.
1044 */
1045 public static Filter createExtensibleMatchFilter(final String attributeName,
1046 final String matchingRuleID,
1047 final boolean dnAttributes,
1048 final byte[] assertionValue)
1049 {
1050 ensureNotNull(assertionValue);
1051 ensureFalse((attributeName == null) && (matchingRuleID == null));
1052
1053 return new Filter(null, FILTER_TYPE_EXTENSIBLE_MATCH, NO_FILTERS, null,
1054 attributeName, new ASN1OctetString(assertionValue), null,
1055 NO_SUB_ANY, null, matchingRuleID, dnAttributes);
1056 }
1057
1058
1059
1060 /**
1061 * Creates a new extensible match search filter with the provided
1062 * information. At least one of the attribute name and matching rule ID must
1063 * be specified, and the assertion value must always be present.
1064 *
1065 * @param attributeName The attribute name for this extensible match
1066 * filter.
1067 * @param matchingRuleID The matching rule ID for this extensible match
1068 * filter.
1069 * @param dnAttributes Indicates whether the match should be performed
1070 * against attributes in the target entry's DN.
1071 * @param assertionValue The assertion value for this extensible match
1072 * filter. It must not be {@code null}.
1073 *
1074 * @return The created approximate match search filter.
1075 */
1076 static Filter createExtensibleMatchFilter(final String attributeName,
1077 final String matchingRuleID, final boolean dnAttributes,
1078 final ASN1OctetString assertionValue)
1079 {
1080 ensureNotNull(assertionValue);
1081 ensureFalse((attributeName == null) && (matchingRuleID == null));
1082
1083 return new Filter(null, FILTER_TYPE_EXTENSIBLE_MATCH, NO_FILTERS, null,
1084 attributeName, assertionValue, null, NO_SUB_ANY, null,
1085 matchingRuleID, dnAttributes);
1086 }
1087
1088
1089
1090 /**
1091 * Creates a new search filter from the provided string representation.
1092 *
1093 * @param filterString The string representation of the filter to create.
1094 * It must not be {@code null}.
1095 *
1096 * @return The search filter decoded from the provided filter string.
1097 *
1098 * @throws LDAPException If the provided string cannot be decoded as a valid
1099 * LDAP search filter.
1100 */
1101 public static Filter create(final String filterString)
1102 throws LDAPException
1103 {
1104 ensureNotNull(filterString);
1105
1106 return create(filterString, 0, (filterString.length() - 1), 0);
1107 }
1108
1109
1110
1111 /**
1112 * Creates a new search filter from the specified portion of the provided
1113 * string representation.
1114 *
1115 * @param filterString The string representation of the filter to create.
1116 * @param startPos The position of the first character to consider as
1117 * part of the filter.
1118 * @param endPos The position of the last character to consider as
1119 * part of the filter.
1120 * @param depth The current nesting depth for this filter. It should
1121 * be increased by one for each AND, OR, or NOT filter
1122 * encountered, in order to prevent stack overflow
1123 * errors from excessive recursion.
1124 *
1125 * @return The decoded search filter.
1126 *
1127 * @throws LDAPException If the provided string cannot be decoded as a valid
1128 * LDAP search filter.
1129 */
1130 private static Filter create(final String filterString, final int startPos,
1131 final int endPos, final int depth)
1132 throws LDAPException
1133 {
1134 if (depth > 50)
1135 {
1136 throw new LDAPException(ResultCode.FILTER_ERROR,
1137 ERR_FILTER_TOO_DEEP.get());
1138 }
1139
1140 final byte filterType;
1141 final Filter[] filterComps;
1142 final Filter notComp;
1143 final String attrName;
1144 final ASN1OctetString assertionValue;
1145 final ASN1OctetString subInitial;
1146 final ASN1OctetString[] subAny;
1147 final ASN1OctetString subFinal;
1148 final String matchingRuleID;
1149 final boolean dnAttributes;
1150
1151 if (startPos >= endPos)
1152 {
1153 throw new LDAPException(ResultCode.FILTER_ERROR,
1154 ERR_FILTER_TOO_SHORT.get());
1155 }
1156
1157 int l = startPos;
1158 int r = endPos;
1159
1160 // First, see if the provided filter string is enclosed in parentheses, like
1161 // it should be. If so, then strip off the outer parentheses.
1162 if (filterString.charAt(l) == '(')
1163 {
1164 if (filterString.charAt(r) == ')')
1165 {
1166 l++;
1167 r--;
1168 }
1169 else
1170 {
1171 throw new LDAPException(ResultCode.FILTER_ERROR,
1172 ERR_FILTER_OPEN_WITHOUT_CLOSE.get(l, r));
1173 }
1174 }
1175 else
1176 {
1177 // This is technically an error, and it's a bad practice. If we're
1178 // working on the complete filter string then we'll let it slide, but
1179 // otherwise we'll raise an error.
1180 if (l != 0)
1181 {
1182 throw new LDAPException(ResultCode.FILTER_ERROR,
1183 ERR_FILTER_MISSING_PARENTHESES.get(
1184 filterString.substring(l, r+1)));
1185 }
1186 }
1187
1188
1189 // Look at the first character of the filter to see if it's an '&', '|', or
1190 // '!'. If we find a parenthesis, then that's an error.
1191 switch (filterString.charAt(l))
1192 {
1193 case '&':
1194 filterType = FILTER_TYPE_AND;
1195 filterComps = parseFilterComps(filterString, l+1, r, depth+1);
1196 notComp = null;
1197 attrName = null;
1198 assertionValue = null;
1199 subInitial = null;
1200 subAny = NO_SUB_ANY;
1201 subFinal = null;
1202 matchingRuleID = null;
1203 dnAttributes = false;
1204 break;
1205
1206 case '|':
1207 filterType = FILTER_TYPE_OR;
1208 filterComps = parseFilterComps(filterString, l+1, r, depth+1);
1209 notComp = null;
1210 attrName = null;
1211 assertionValue = null;
1212 subInitial = null;
1213 subAny = NO_SUB_ANY;
1214 subFinal = null;
1215 matchingRuleID = null;
1216 dnAttributes = false;
1217 break;
1218
1219 case '!':
1220 filterType = FILTER_TYPE_NOT;
1221 filterComps = NO_FILTERS;
1222 notComp = create(filterString, l+1, r, depth+1);
1223 attrName = null;
1224 assertionValue = null;
1225 subInitial = null;
1226 subAny = NO_SUB_ANY;
1227 subFinal = null;
1228 matchingRuleID = null;
1229 dnAttributes = false;
1230 break;
1231
1232 case '(':
1233 throw new LDAPException(ResultCode.FILTER_ERROR,
1234 ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(l));
1235
1236 case ':':
1237 // This must be an extensible matching filter that starts with a
1238 // dnAttributes flag and/or matching rule ID, and we should parse it
1239 // accordingly.
1240 filterType = FILTER_TYPE_EXTENSIBLE_MATCH;
1241 filterComps = NO_FILTERS;
1242 notComp = null;
1243 attrName = null;
1244 subInitial = null;
1245 subAny = NO_SUB_ANY;
1246 subFinal = null;
1247
1248 // The next element must be either the "dn:{matchingruleid}" or just
1249 // "{matchingruleid}", and it must be followed by a colon.
1250 final int dnMRIDStart = ++l;
1251 while ((l <= r) && (filterString.charAt(l) != ':'))
1252 {
1253 l++;
1254 }
1255
1256 if (l > r)
1257 {
1258 throw new LDAPException(ResultCode.FILTER_ERROR,
1259 ERR_FILTER_NO_COLON_AFTER_MRID.get(
1260 startPos));
1261 }
1262 else if (l == dnMRIDStart)
1263 {
1264 throw new LDAPException(ResultCode.FILTER_ERROR,
1265 ERR_FILTER_EMPTY_MRID.get(startPos));
1266 }
1267 final String s = filterString.substring(dnMRIDStart, l++);
1268 if (s.equalsIgnoreCase("dn"))
1269 {
1270 dnAttributes = true;
1271
1272 // The colon must be followed by the matching rule ID and another
1273 // colon.
1274 final int mrIDStart = l;
1275 while ((l < r) && (filterString.charAt(l) != ':'))
1276 {
1277 l++;
1278 }
1279
1280 if (l >= r)
1281 {
1282 throw new LDAPException(ResultCode.FILTER_ERROR,
1283 ERR_FILTER_NO_COLON_AFTER_MRID.get(
1284 startPos));
1285 }
1286
1287 matchingRuleID = filterString.substring(mrIDStart, l);
1288 if (matchingRuleID.length() == 0)
1289 {
1290 throw new LDAPException(ResultCode.FILTER_ERROR,
1291 ERR_FILTER_EMPTY_MRID.get(startPos));
1292 }
1293
1294 if ((++l > r) || (filterString.charAt(l) != '='))
1295 {
1296 throw new LDAPException(ResultCode.FILTER_ERROR,
1297 ERR_FILTER_UNEXPECTED_CHAR_AFTER_MRID.get(
1298 filterString.charAt(l), startPos));
1299 }
1300 }
1301 else
1302 {
1303 matchingRuleID = s;
1304 dnAttributes = false;
1305
1306 // The colon must be followed by an equal sign.
1307 if ((l > r) || (filterString.charAt(l) != '='))
1308 {
1309 throw new LDAPException(ResultCode.FILTER_ERROR,
1310 ERR_FILTER_NO_EQUAL_AFTER_MRID.get(
1311 startPos));
1312 }
1313 }
1314
1315 // Now we should be able to read the value, handling any escape
1316 // characters as we go.
1317 l++;
1318 final ByteStringBuffer valueBuffer = new ByteStringBuffer(r - l + 1);
1319 while (l <= r)
1320 {
1321 final char c = filterString.charAt(l);
1322 if (c == '\\')
1323 {
1324 l = readEscapedHexString(filterString, ++l, valueBuffer);
1325 }
1326 else if (c == '(')
1327 {
1328 throw new LDAPException(ResultCode.FILTER_ERROR,
1329 ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(l));
1330 }
1331 else if (c == ')')
1332 {
1333 throw new LDAPException(ResultCode.FILTER_ERROR,
1334 ERR_FILTER_UNEXPECTED_CLOSE_PAREN.get(l));
1335 }
1336 else
1337 {
1338 valueBuffer.append(c);
1339 l++;
1340 }
1341 }
1342 assertionValue = new ASN1OctetString(valueBuffer.toByteArray());
1343 break;
1344
1345
1346 default:
1347 // We know that it's not an AND, OR, or NOT filter, so we can eliminate
1348 // the variables used only for them.
1349 filterComps = NO_FILTERS;
1350 notComp = null;
1351
1352
1353 // We should now be able to read a non-empty attribute name.
1354 final int attrStartPos = l;
1355 int attrEndPos = -1;
1356 byte tempFilterType = 0x00;
1357 boolean filterTypeKnown = false;
1358 attrNameLoop:
1359 while (l <= r)
1360 {
1361 final char c = filterString.charAt(l++);
1362 switch (c)
1363 {
1364 case ':':
1365 tempFilterType = FILTER_TYPE_EXTENSIBLE_MATCH;
1366 filterTypeKnown = true;
1367 attrEndPos = l - 1;
1368 break attrNameLoop;
1369
1370 case '>':
1371 tempFilterType = FILTER_TYPE_GREATER_OR_EQUAL;
1372 filterTypeKnown = true;
1373 attrEndPos = l - 1;
1374
1375 if (l <= r)
1376 {
1377 if (filterString.charAt(l++) != '=')
1378 {
1379 throw new LDAPException(ResultCode.FILTER_ERROR,
1380 ERR_FILTER_UNEXPECTED_CHAR_AFTER_GT.get(
1381 startPos, filterString.charAt(l-1)));
1382 }
1383 }
1384 else
1385 {
1386 throw new LDAPException(ResultCode.FILTER_ERROR,
1387 ERR_FILTER_END_AFTER_GT.get(startPos));
1388 }
1389 break attrNameLoop;
1390
1391 case '<':
1392 tempFilterType = FILTER_TYPE_LESS_OR_EQUAL;
1393 filterTypeKnown = true;
1394 attrEndPos = l - 1;
1395
1396 if (l <= r)
1397 {
1398 if (filterString.charAt(l++) != '=')
1399 {
1400 throw new LDAPException(ResultCode.FILTER_ERROR,
1401 ERR_FILTER_UNEXPECTED_CHAR_AFTER_LT.get(
1402 startPos, filterString.charAt(l-1)));
1403 }
1404 }
1405 else
1406 {
1407 throw new LDAPException(ResultCode.FILTER_ERROR,
1408 ERR_FILTER_END_AFTER_LT.get(startPos));
1409 }
1410 break attrNameLoop;
1411
1412 case '~':
1413 tempFilterType = FILTER_TYPE_APPROXIMATE_MATCH;
1414 filterTypeKnown = true;
1415 attrEndPos = l - 1;
1416
1417 if (l <= r)
1418 {
1419 if (filterString.charAt(l++) != '=')
1420 {
1421 throw new LDAPException(ResultCode.FILTER_ERROR,
1422 ERR_FILTER_UNEXPECTED_CHAR_AFTER_TILDE.get(
1423 startPos, filterString.charAt(l-1)));
1424 }
1425 }
1426 else
1427 {
1428 throw new LDAPException(ResultCode.FILTER_ERROR,
1429 ERR_FILTER_END_AFTER_TILDE.get(
1430 startPos));
1431 }
1432 break attrNameLoop;
1433
1434 case '=':
1435 // It could be either an equality, presence, or substring filter.
1436 // We'll need to look at the value to determine that.
1437 attrEndPos = l - 1;
1438 break attrNameLoop;
1439 }
1440 }
1441
1442 if (attrEndPos <= attrStartPos)
1443 {
1444 throw new LDAPException(ResultCode.FILTER_ERROR,
1445 ERR_FILTER_EMPTY_ATTR_NAME.get(startPos));
1446 }
1447 attrName = filterString.substring(attrStartPos, attrEndPos);
1448
1449
1450 // See if we're dealing with an extensible match filter. If so, then
1451 // we may still need to do additional parsing to get the matching rule
1452 // ID and/or the dnAttributes flag. Otherwise, we can rule out any
1453 // variables that are specific to extensible matching filters.
1454 if (filterTypeKnown && (tempFilterType == FILTER_TYPE_EXTENSIBLE_MATCH))
1455 {
1456 if (l > r)
1457 {
1458 throw new LDAPException(ResultCode.FILTER_ERROR,
1459 ERR_FILTER_NO_EQUALS.get(startPos));
1460 }
1461
1462 final char c = filterString.charAt(l++);
1463 if (c == '=')
1464 {
1465 matchingRuleID = null;
1466 dnAttributes = false;
1467 }
1468 else
1469 {
1470 // We have either a matching rule ID or a dnAttributes flag, or
1471 // both. Iterate through the filter until we find the equal sign,
1472 // and then figure out what we have from that.
1473 boolean equalFound = false;
1474 final int substrStartPos = l - 1;
1475 while (l <= r)
1476 {
1477 if (filterString.charAt(l++) == '=')
1478 {
1479 equalFound = true;
1480 break;
1481 }
1482 }
1483
1484 if (! equalFound)
1485 {
1486 throw new LDAPException(ResultCode.FILTER_ERROR,
1487 ERR_FILTER_NO_EQUALS.get(startPos));
1488 }
1489
1490 final String substr = filterString.substring(substrStartPos, l-1);
1491 final String lowerSubstr = toLowerCase(substr);
1492 if (! substr.endsWith(":"))
1493 {
1494 throw new LDAPException(ResultCode.FILTER_ERROR,
1495 ERR_FILTER_CANNOT_PARSE_MRID.get(
1496 startPos));
1497 }
1498
1499 if (lowerSubstr.equals("dn:"))
1500 {
1501 matchingRuleID = null;
1502 dnAttributes = true;
1503 }
1504 else if (lowerSubstr.startsWith("dn:"))
1505 {
1506 matchingRuleID = substr.substring(3, substr.length() - 1);
1507 if (matchingRuleID.length() == 0)
1508 {
1509 throw new LDAPException(ResultCode.FILTER_ERROR,
1510 ERR_FILTER_EMPTY_MRID.get(startPos));
1511 }
1512
1513 dnAttributes = true;
1514 }
1515 else
1516 {
1517 matchingRuleID = substr.substring(0, substr.length() - 1);
1518 dnAttributes = false;
1519
1520 if (matchingRuleID.length() == 0)
1521 {
1522 throw new LDAPException(ResultCode.FILTER_ERROR,
1523 ERR_FILTER_EMPTY_MRID.get(startPos));
1524 }
1525 }
1526 }
1527 }
1528 else
1529 {
1530 matchingRuleID = null;
1531 dnAttributes = false;
1532 }
1533
1534
1535 // At this point, we're ready to read the value. If we still don't
1536 // know what type of filter we're dealing with, then we can tell that
1537 // based on asterisks in the value.
1538 if (l > r)
1539 {
1540 assertionValue = new ASN1OctetString();
1541 if (! filterTypeKnown)
1542 {
1543 tempFilterType = FILTER_TYPE_EQUALITY;
1544 }
1545
1546 subInitial = null;
1547 subAny = NO_SUB_ANY;
1548 subFinal = null;
1549 }
1550 else if (l == r)
1551 {
1552 if (filterTypeKnown)
1553 {
1554 switch (filterString.charAt(l))
1555 {
1556 case '*':
1557 case '(':
1558 case ')':
1559 case '\\':
1560 throw new LDAPException(ResultCode.FILTER_ERROR,
1561 ERR_FILTER_UNEXPECTED_CHAR_IN_AV.get(
1562 filterString.charAt(l), startPos));
1563 }
1564
1565 assertionValue =
1566 new ASN1OctetString(filterString.substring(l, l+1));
1567 }
1568 else
1569 {
1570 final char c = filterString.charAt(l);
1571 switch (c)
1572 {
1573 case '*':
1574 tempFilterType = FILTER_TYPE_PRESENCE;
1575 assertionValue = null;
1576 break;
1577
1578 case '\\':
1579 case '(':
1580 case ')':
1581 throw new LDAPException(ResultCode.FILTER_ERROR,
1582 ERR_FILTER_UNEXPECTED_CHAR_IN_AV.get(
1583 filterString.charAt(l), startPos));
1584
1585 default:
1586 tempFilterType = FILTER_TYPE_EQUALITY;
1587 assertionValue =
1588 new ASN1OctetString(filterString.substring(l, l+1));
1589 break;
1590 }
1591 }
1592
1593 subInitial = null;
1594 subAny = NO_SUB_ANY;
1595 subFinal = null;
1596 }
1597 else
1598 {
1599 if (! filterTypeKnown)
1600 {
1601 tempFilterType = FILTER_TYPE_EQUALITY;
1602 }
1603
1604 final int valueStartPos = l;
1605 ASN1OctetString tempSubInitial = null;
1606 ASN1OctetString tempSubFinal = null;
1607 final ArrayList<ASN1OctetString> subAnyList =
1608 new ArrayList<ASN1OctetString>(1);
1609 ByteStringBuffer buffer = new ByteStringBuffer(r - l + 1);
1610 while (l <= r)
1611 {
1612 final char c = filterString.charAt(l++);
1613 switch (c)
1614 {
1615 case '*':
1616 if (filterTypeKnown)
1617 {
1618 throw new LDAPException(ResultCode.FILTER_ERROR,
1619 ERR_FILTER_UNEXPECTED_ASTERISK.get(
1620 startPos));
1621 }
1622 else
1623 {
1624 if ((l-1) == valueStartPos)
1625 {
1626 // The first character is an asterisk, so there is no
1627 // subInitial.
1628 }
1629 else
1630 {
1631 if (tempFilterType == FILTER_TYPE_SUBSTRING)
1632 {
1633 // We already know that it's a substring filter, so this
1634 // must be a subAny portion. However, if the buffer is
1635 // empty, then that means that there were two asterisks
1636 // right next to each other, which is invalid.
1637 if (buffer.length() == 0)
1638 {
1639 throw new LDAPException(ResultCode.FILTER_ERROR,
1640 ERR_FILTER_UNEXPECTED_DOUBLE_ASTERISK.get(
1641 startPos));
1642 }
1643 else
1644 {
1645 subAnyList.add(
1646 new ASN1OctetString(buffer.toByteArray()));
1647 buffer = new ByteStringBuffer(r - l + 1);
1648 }
1649 }
1650 else
1651 {
1652 // We haven't yet set the filter type, so the buffer must
1653 // contain the subInitial portion. We also know it's not
1654 // empty because of an earlier check.
1655 tempSubInitial =
1656 new ASN1OctetString(buffer.toByteArray());
1657 buffer = new ByteStringBuffer(r - l + 1);
1658 }
1659 }
1660
1661 tempFilterType = FILTER_TYPE_SUBSTRING;
1662 }
1663 break;
1664
1665 case '\\':
1666 l = readEscapedHexString(filterString, l, buffer);
1667 break;
1668
1669 case '(':
1670 throw new LDAPException(ResultCode.FILTER_ERROR,
1671 ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(
1672 l));
1673
1674 case ')':
1675 throw new LDAPException(ResultCode.FILTER_ERROR,
1676 ERR_FILTER_UNEXPECTED_CLOSE_PAREN.get(
1677 l));
1678
1679 default:
1680 buffer.append(c);
1681 break;
1682 }
1683 }
1684
1685 if ((tempFilterType == FILTER_TYPE_SUBSTRING) &&
1686 (buffer.length() > 0))
1687 {
1688 // The buffer must contain the subFinal portion.
1689 tempSubFinal = new ASN1OctetString(buffer.toByteArray());
1690 }
1691
1692 subInitial = tempSubInitial;
1693 subAny = subAnyList.toArray(new ASN1OctetString[subAnyList.size()]);
1694 subFinal = tempSubFinal;
1695
1696 if (tempFilterType == FILTER_TYPE_SUBSTRING)
1697 {
1698 assertionValue = null;
1699 }
1700 else
1701 {
1702 assertionValue = new ASN1OctetString(buffer.toByteArray());
1703 }
1704 }
1705
1706 filterType = tempFilterType;
1707 break;
1708 }
1709
1710
1711 if (startPos == 0)
1712 {
1713 return new Filter(filterString, filterType, filterComps, notComp,
1714 attrName, assertionValue, subInitial, subAny, subFinal,
1715 matchingRuleID, dnAttributes);
1716 }
1717 else
1718 {
1719 return new Filter(filterString.substring(startPos, endPos+1), filterType,
1720 filterComps, notComp, attrName, assertionValue,
1721 subInitial, subAny, subFinal, matchingRuleID,
1722 dnAttributes);
1723 }
1724 }
1725
1726
1727
1728 /**
1729 * Parses the specified portion of the provided filter string to obtain a set
1730 * of filter components for use in an AND or OR filter.
1731 *
1732 * @param filterString The string representation for the set of filters.
1733 * @param startPos The position of the first character to consider as
1734 * part of the first filter.
1735 * @param endPos The position of the last character to consider as
1736 * part of the last filter.
1737 * @param depth The current nesting depth for this filter. It should
1738 * be increased by one for each AND, OR, or NOT filter
1739 * encountered, in order to prevent stack overflow
1740 * errors from excessive recursion.
1741 *
1742 * @return The decoded set of search filters.
1743 *
1744 * @throws LDAPException If the provided string cannot be decoded as a set
1745 * of LDAP search filters.
1746 */
1747 private static Filter[] parseFilterComps(final String filterString,
1748 final int startPos, final int endPos,
1749 final int depth)
1750 throws LDAPException
1751 {
1752 if (startPos > endPos)
1753 {
1754 // This is acceptable, since it can represent an LDAP TRUE or FALSE filter
1755 // as described in RFC 4526.
1756 return NO_FILTERS;
1757 }
1758
1759
1760 // The set of filters must start with an opening parenthesis, and end with a
1761 // closing parenthesis.
1762 if (filterString.charAt(startPos) != '(')
1763 {
1764 throw new LDAPException(ResultCode.FILTER_ERROR,
1765 ERR_FILTER_EXPECTED_OPEN_PAREN.get(startPos));
1766 }
1767 if (filterString.charAt(endPos) != ')')
1768 {
1769 throw new LDAPException(ResultCode.FILTER_ERROR,
1770 ERR_FILTER_EXPECTED_CLOSE_PAREN.get(startPos));
1771 }
1772
1773
1774 // Iterate through the specified portion of the filter string and count
1775 // opening and closing parentheses to figure out where one filter ends and
1776 // another begins.
1777 final ArrayList<Filter> filterList = new ArrayList<Filter>(5);
1778 int filterStartPos = startPos;
1779 int pos = startPos;
1780 int numOpen = 0;
1781 while (pos <= endPos)
1782 {
1783 final char c = filterString.charAt(pos++);
1784 if (c == '(')
1785 {
1786 numOpen++;
1787 }
1788 else if (c == ')')
1789 {
1790 numOpen--;
1791 if (numOpen == 0)
1792 {
1793 filterList.add(create(filterString, filterStartPos, pos-1, depth));
1794 filterStartPos = pos;
1795 }
1796 }
1797 }
1798
1799 if (numOpen != 0)
1800 {
1801 throw new LDAPException(ResultCode.FILTER_ERROR,
1802 ERR_FILTER_MISMATCHED_PARENS.get(startPos,
1803 endPos));
1804 }
1805
1806 return filterList.toArray(new Filter[filterList.size()]);
1807 }
1808
1809
1810
1811 /**
1812 * Reads one or more hex-encoded bytes from the specified portion of the
1813 * filter string.
1814 *
1815 * @param filterString The string from which the data is to be read.
1816 * @param startPos The position at which to start reading. This should
1817 * be the position of first hex character immediately
1818 * after the initial backslash.
1819 * @param buffer The buffer to which the decoded string portion should
1820 * be appended.
1821 *
1822 * @return The position at which the caller may resume parsing.
1823 *
1824 * @throws LDAPException If a problem occurs while reading hex-encoded
1825 * bytes.
1826 */
1827 private static int readEscapedHexString(final String filterString,
1828 final int startPos,
1829 final ByteStringBuffer buffer)
1830 throws LDAPException
1831 {
1832 byte b;
1833 switch (filterString.charAt(startPos))
1834 {
1835 case '0':
1836 b = 0x00;
1837 break;
1838 case '1':
1839 b = 0x10;
1840 break;
1841 case '2':
1842 b = 0x20;
1843 break;
1844 case '3':
1845 b = 0x30;
1846 break;
1847 case '4':
1848 b = 0x40;
1849 break;
1850 case '5':
1851 b = 0x50;
1852 break;
1853 case '6':
1854 b = 0x60;
1855 break;
1856 case '7':
1857 b = 0x70;
1858 break;
1859 case '8':
1860 b = (byte) 0x80;
1861 break;
1862 case '9':
1863 b = (byte) 0x90;
1864 break;
1865 case 'a':
1866 case 'A':
1867 b = (byte) 0xA0;
1868 break;
1869 case 'b':
1870 case 'B':
1871 b = (byte) 0xB0;
1872 break;
1873 case 'c':
1874 case 'C':
1875 b = (byte) 0xC0;
1876 break;
1877 case 'd':
1878 case 'D':
1879 b = (byte) 0xD0;
1880 break;
1881 case 'e':
1882 case 'E':
1883 b = (byte) 0xE0;
1884 break;
1885 case 'f':
1886 case 'F':
1887 b = (byte) 0xF0;
1888 break;
1889 default:
1890 throw new LDAPException(ResultCode.FILTER_ERROR,
1891 ERR_FILTER_INVALID_HEX_CHAR.get(filterString.charAt(startPos),
1892 startPos));
1893 }
1894
1895 switch (filterString.charAt(startPos+1))
1896 {
1897 case '0':
1898 // No action is required.
1899 break;
1900 case '1':
1901 b |= 0x01;
1902 break;
1903 case '2':
1904 b |= 0x02;
1905 break;
1906 case '3':
1907 b |= 0x03;
1908 break;
1909 case '4':
1910 b |= 0x04;
1911 break;
1912 case '5':
1913 b |= 0x05;
1914 break;
1915 case '6':
1916 b |= 0x06;
1917 break;
1918 case '7':
1919 b |= 0x07;
1920 break;
1921 case '8':
1922 b |= 0x08;
1923 break;
1924 case '9':
1925 b |= 0x09;
1926 break;
1927 case 'a':
1928 case 'A':
1929 b |= 0x0A;
1930 break;
1931 case 'b':
1932 case 'B':
1933 b |= 0x0B;
1934 break;
1935 case 'c':
1936 case 'C':
1937 b |= 0x0C;
1938 break;
1939 case 'd':
1940 case 'D':
1941 b |= 0x0D;
1942 break;
1943 case 'e':
1944 case 'E':
1945 b |= 0x0E;
1946 break;
1947 case 'f':
1948 case 'F':
1949 b |= 0x0F;
1950 break;
1951 default:
1952 throw new LDAPException(ResultCode.FILTER_ERROR,
1953 ERR_FILTER_INVALID_HEX_CHAR.get(filterString.charAt(startPos+1),
1954 (startPos+1)));
1955 }
1956
1957 buffer.append(b);
1958 return startPos+2;
1959 }
1960
1961
1962
1963 /**
1964 * Writes an ASN.1-encoded representation of this filter to the provided ASN.1
1965 * buffer.
1966 *
1967 * @param buffer The ASN.1 buffer to which the encoded representation should
1968 * be written.
1969 */
1970 public void writeTo(final ASN1Buffer buffer)
1971 {
1972 switch (filterType)
1973 {
1974 case FILTER_TYPE_AND:
1975 case FILTER_TYPE_OR:
1976 final ASN1BufferSet compSet = buffer.beginSet(filterType);
1977 for (final Filter f : filterComps)
1978 {
1979 f.writeTo(buffer);
1980 }
1981 compSet.end();
1982 break;
1983
1984 case FILTER_TYPE_NOT:
1985 buffer.addElement(
1986 new ASN1Element(filterType, notComp.encode().encode()));
1987 break;
1988
1989 case FILTER_TYPE_EQUALITY:
1990 case FILTER_TYPE_GREATER_OR_EQUAL:
1991 case FILTER_TYPE_LESS_OR_EQUAL:
1992 case FILTER_TYPE_APPROXIMATE_MATCH:
1993 final ASN1BufferSequence avaSequence = buffer.beginSequence(filterType);
1994 buffer.addOctetString(attrName);
1995 buffer.addElement(assertionValue);
1996 avaSequence.end();
1997 break;
1998
1999 case FILTER_TYPE_SUBSTRING:
2000 final ASN1BufferSequence subFilterSequence =
2001 buffer.beginSequence(filterType);
2002 buffer.addOctetString(attrName);
2003
2004 final ASN1BufferSequence valueSequence = buffer.beginSequence();
2005 if (subInitial != null)
2006 {
2007 buffer.addOctetString(SUBSTRING_TYPE_SUBINITIAL,
2008 subInitial.getValue());
2009 }
2010
2011 for (final ASN1OctetString s : subAny)
2012 {
2013 buffer.addOctetString(SUBSTRING_TYPE_SUBANY, s.getValue());
2014 }
2015
2016 if (subFinal != null)
2017 {
2018 buffer.addOctetString(SUBSTRING_TYPE_SUBFINAL, subFinal.getValue());
2019 }
2020 valueSequence.end();
2021 subFilterSequence.end();
2022 break;
2023
2024 case FILTER_TYPE_PRESENCE:
2025 buffer.addOctetString(filterType, attrName);
2026 break;
2027
2028 case FILTER_TYPE_EXTENSIBLE_MATCH:
2029 final ASN1BufferSequence mrSequence = buffer.beginSequence(filterType);
2030 if (matchingRuleID != null)
2031 {
2032 buffer.addOctetString(EXTENSIBLE_TYPE_MATCHING_RULE_ID,
2033 matchingRuleID);
2034 }
2035
2036 if (attrName != null)
2037 {
2038 buffer.addOctetString(EXTENSIBLE_TYPE_ATTRIBUTE_NAME, attrName);
2039 }
2040
2041 buffer.addOctetString(EXTENSIBLE_TYPE_MATCH_VALUE,
2042 assertionValue.getValue());
2043
2044 if (dnAttributes)
2045 {
2046 buffer.addBoolean(EXTENSIBLE_TYPE_DN_ATTRIBUTES, true);
2047 }
2048 mrSequence.end();
2049 break;
2050 }
2051 }
2052
2053
2054
2055 /**
2056 * Encodes this search filter to an ASN.1 element suitable for inclusion in an
2057 * LDAP search request protocol op.
2058 *
2059 * @return An ASN.1 element containing the encoded search filter.
2060 */
2061 public ASN1Element encode()
2062 {
2063 switch (filterType)
2064 {
2065 case FILTER_TYPE_AND:
2066 case FILTER_TYPE_OR:
2067 final ASN1Element[] filterElements =
2068 new ASN1Element[filterComps.length];
2069 for (int i=0; i < filterComps.length; i++)
2070 {
2071 filterElements[i] = filterComps[i].encode();
2072 }
2073 return new ASN1Set(filterType, filterElements);
2074
2075
2076 case FILTER_TYPE_NOT:
2077 return new ASN1Element(filterType, notComp.encode().encode());
2078
2079
2080 case FILTER_TYPE_EQUALITY:
2081 case FILTER_TYPE_GREATER_OR_EQUAL:
2082 case FILTER_TYPE_LESS_OR_EQUAL:
2083 case FILTER_TYPE_APPROXIMATE_MATCH:
2084 final ASN1OctetString[] attrValueAssertionElements =
2085 {
2086 new ASN1OctetString(attrName),
2087 assertionValue
2088 };
2089 return new ASN1Sequence(filterType, attrValueAssertionElements);
2090
2091
2092 case FILTER_TYPE_SUBSTRING:
2093 final ArrayList<ASN1OctetString> subList =
2094 new ArrayList<ASN1OctetString>(2 + subAny.length);
2095 if (subInitial != null)
2096 {
2097 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBINITIAL,
2098 subInitial.getValue()));
2099 }
2100
2101 for (final ASN1Element subAnyElement : subAny)
2102 {
2103 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBANY,
2104 subAnyElement.getValue()));
2105 }
2106
2107
2108 if (subFinal != null)
2109 {
2110 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBFINAL,
2111 subFinal.getValue()));
2112 }
2113
2114 final ASN1Element[] subFilterElements =
2115 {
2116 new ASN1OctetString(attrName),
2117 new ASN1Sequence(subList)
2118 };
2119 return new ASN1Sequence(filterType, subFilterElements);
2120
2121
2122 case FILTER_TYPE_PRESENCE:
2123 return new ASN1OctetString(filterType, attrName);
2124
2125
2126 case FILTER_TYPE_EXTENSIBLE_MATCH:
2127 final ArrayList<ASN1Element> emElementList =
2128 new ArrayList<ASN1Element>(4);
2129 if (matchingRuleID != null)
2130 {
2131 emElementList.add(new ASN1OctetString(
2132 EXTENSIBLE_TYPE_MATCHING_RULE_ID, matchingRuleID));
2133 }
2134
2135 if (attrName != null)
2136 {
2137 emElementList.add(new ASN1OctetString(
2138 EXTENSIBLE_TYPE_ATTRIBUTE_NAME, attrName));
2139 }
2140
2141 emElementList.add(new ASN1OctetString(EXTENSIBLE_TYPE_MATCH_VALUE,
2142 assertionValue.getValue()));
2143
2144 if (dnAttributes)
2145 {
2146 emElementList.add(new ASN1Boolean(EXTENSIBLE_TYPE_DN_ATTRIBUTES,
2147 true));
2148 }
2149
2150 return new ASN1Sequence(filterType, emElementList);
2151
2152
2153 default:
2154 throw new AssertionError(ERR_FILTER_INVALID_TYPE.get(
2155 toHex(filterType)));
2156 }
2157 }
2158
2159
2160
2161 /**
2162 * Reads and decodes a search filter from the provided ASN.1 stream reader.
2163 *
2164 * @param reader The ASN.1 stream reader from which to read the filter.
2165 *
2166 * @return The decoded search filter.
2167 *
2168 * @throws LDAPException If an error occurs while reading or parsing the
2169 * search filter.
2170 */
2171 public static Filter readFrom(final ASN1StreamReader reader)
2172 throws LDAPException
2173 {
2174 try
2175 {
2176 final Filter[] filterComps;
2177 final Filter notComp;
2178 final String attrName;
2179 final ASN1OctetString assertionValue;
2180 final ASN1OctetString subInitial;
2181 final ASN1OctetString[] subAny;
2182 final ASN1OctetString subFinal;
2183 final String matchingRuleID;
2184 final boolean dnAttributes;
2185
2186 final byte filterType = (byte) reader.peek();
2187
2188 switch (filterType)
2189 {
2190 case FILTER_TYPE_AND:
2191 case FILTER_TYPE_OR:
2192 final ArrayList<Filter> comps = new ArrayList<Filter>(5);
2193 final ASN1StreamReaderSet elementSet = reader.beginSet();
2194 while (elementSet.hasMoreElements())
2195 {
2196 comps.add(readFrom(reader));
2197 }
2198
2199 filterComps = new Filter[comps.size()];
2200 comps.toArray(filterComps);
2201
2202 notComp = null;
2203 attrName = null;
2204 assertionValue = null;
2205 subInitial = null;
2206 subAny = NO_SUB_ANY;
2207 subFinal = null;
2208 matchingRuleID = null;
2209 dnAttributes = false;
2210 break;
2211
2212
2213 case FILTER_TYPE_NOT:
2214 final ASN1Element notFilterElement;
2215 try
2216 {
2217 final ASN1Element e = reader.readElement();
2218 notFilterElement = ASN1Element.decode(e.getValue());
2219 }
2220 catch (final ASN1Exception ae)
2221 {
2222 debugException(ae);
2223 throw new LDAPException(ResultCode.DECODING_ERROR,
2224 ERR_FILTER_CANNOT_DECODE_NOT_COMP.get(getExceptionMessage(ae)),
2225 ae);
2226 }
2227 notComp = decode(notFilterElement);
2228
2229 filterComps = NO_FILTERS;
2230 attrName = null;
2231 assertionValue = null;
2232 subInitial = null;
2233 subAny = NO_SUB_ANY;
2234 subFinal = null;
2235 matchingRuleID = null;
2236 dnAttributes = false;
2237 break;
2238
2239
2240 case FILTER_TYPE_EQUALITY:
2241 case FILTER_TYPE_GREATER_OR_EQUAL:
2242 case FILTER_TYPE_LESS_OR_EQUAL:
2243 case FILTER_TYPE_APPROXIMATE_MATCH:
2244 reader.beginSequence();
2245 attrName = reader.readString();
2246 assertionValue = new ASN1OctetString(reader.readBytes());
2247
2248 filterComps = NO_FILTERS;
2249 notComp = null;
2250 subInitial = null;
2251 subAny = NO_SUB_ANY;
2252 subFinal = null;
2253 matchingRuleID = null;
2254 dnAttributes = false;
2255 break;
2256
2257
2258 case FILTER_TYPE_SUBSTRING:
2259 reader.beginSequence();
2260 attrName = reader.readString();
2261
2262 ASN1OctetString tempSubInitial = null;
2263 ASN1OctetString tempSubFinal = null;
2264 final ArrayList<ASN1OctetString> subAnyList =
2265 new ArrayList<ASN1OctetString>(1);
2266 final ASN1StreamReaderSequence subSequence = reader.beginSequence();
2267 while (subSequence.hasMoreElements())
2268 {
2269 final byte type = (byte) reader.peek();
2270 final ASN1OctetString s =
2271 new ASN1OctetString(type, reader.readBytes());
2272 switch (type)
2273 {
2274 case SUBSTRING_TYPE_SUBINITIAL:
2275 tempSubInitial = s;
2276 break;
2277 case SUBSTRING_TYPE_SUBANY:
2278 subAnyList.add(s);
2279 break;
2280 case SUBSTRING_TYPE_SUBFINAL:
2281 tempSubFinal = s;
2282 break;
2283 default:
2284 throw new LDAPException(ResultCode.DECODING_ERROR,
2285 ERR_FILTER_INVALID_SUBSTR_TYPE.get(toHex(type)));
2286 }
2287 }
2288
2289 subInitial = tempSubInitial;
2290 subFinal = tempSubFinal;
2291
2292 subAny = new ASN1OctetString[subAnyList.size()];
2293 subAnyList.toArray(subAny);
2294
2295 filterComps = NO_FILTERS;
2296 notComp = null;
2297 assertionValue = null;
2298 matchingRuleID = null;
2299 dnAttributes = false;
2300 break;
2301
2302
2303 case FILTER_TYPE_PRESENCE:
2304 attrName = reader.readString();
2305
2306 filterComps = NO_FILTERS;
2307 notComp = null;
2308 assertionValue = null;
2309 subInitial = null;
2310 subAny = NO_SUB_ANY;
2311 subFinal = null;
2312 matchingRuleID = null;
2313 dnAttributes = false;
2314 break;
2315
2316
2317 case FILTER_TYPE_EXTENSIBLE_MATCH:
2318 String tempAttrName = null;
2319 ASN1OctetString tempAssertionValue = null;
2320 String tempMatchingRuleID = null;
2321 boolean tempDNAttributes = false;
2322
2323 final ASN1StreamReaderSequence emSequence = reader.beginSequence();
2324 while (emSequence.hasMoreElements())
2325 {
2326 final byte type = (byte) reader.peek();
2327 switch (type)
2328 {
2329 case EXTENSIBLE_TYPE_ATTRIBUTE_NAME:
2330 tempAttrName = reader.readString();
2331 break;
2332 case EXTENSIBLE_TYPE_MATCHING_RULE_ID:
2333 tempMatchingRuleID = reader.readString();
2334 break;
2335 case EXTENSIBLE_TYPE_MATCH_VALUE:
2336 tempAssertionValue =
2337 new ASN1OctetString(type, reader.readBytes());
2338 break;
2339 case EXTENSIBLE_TYPE_DN_ATTRIBUTES:
2340 tempDNAttributes = reader.readBoolean();
2341 break;
2342 default:
2343 throw new LDAPException(ResultCode.DECODING_ERROR,
2344 ERR_FILTER_EXTMATCH_INVALID_TYPE.get(toHex(type)));
2345 }
2346 }
2347
2348 if ((tempAttrName == null) && (tempMatchingRuleID == null))
2349 {
2350 throw new LDAPException(ResultCode.DECODING_ERROR,
2351 ERR_FILTER_EXTMATCH_NO_ATTR_OR_MRID.get());
2352 }
2353
2354 if (tempAssertionValue == null)
2355 {
2356 throw new LDAPException(ResultCode.DECODING_ERROR,
2357 ERR_FILTER_EXTMATCH_NO_VALUE.get());
2358 }
2359
2360 attrName = tempAttrName;
2361 assertionValue = tempAssertionValue;
2362 matchingRuleID = tempMatchingRuleID;
2363 dnAttributes = tempDNAttributes;
2364
2365 filterComps = NO_FILTERS;
2366 notComp = null;
2367 subInitial = null;
2368 subAny = NO_SUB_ANY;
2369 subFinal = null;
2370 break;
2371
2372
2373 default:
2374 throw new LDAPException(ResultCode.DECODING_ERROR,
2375 ERR_FILTER_ELEMENT_INVALID_TYPE.get(toHex(filterType)));
2376 }
2377
2378 return new Filter(null, filterType, filterComps, notComp, attrName,
2379 assertionValue, subInitial, subAny, subFinal,
2380 matchingRuleID, dnAttributes);
2381 }
2382 catch (LDAPException le)
2383 {
2384 debugException(le);
2385 throw le;
2386 }
2387 catch (Exception e)
2388 {
2389 debugException(e);
2390 throw new LDAPException(ResultCode.DECODING_ERROR,
2391 ERR_FILTER_CANNOT_DECODE.get(getExceptionMessage(e)), e);
2392 }
2393 }
2394
2395
2396
2397 /**
2398 * Decodes the provided ASN.1 element as a search filter.
2399 *
2400 * @param filterElement The ASN.1 element containing the encoded search
2401 * filter.
2402 *
2403 * @return The decoded search filter.
2404 *
2405 * @throws LDAPException If the provided ASN.1 element cannot be decoded as
2406 * a search filter.
2407 */
2408 public static Filter decode(final ASN1Element filterElement)
2409 throws LDAPException
2410 {
2411 final byte filterType = filterElement.getType();
2412 final Filter[] filterComps;
2413 final Filter notComp;
2414 final String attrName;
2415 final ASN1OctetString assertionValue;
2416 final ASN1OctetString subInitial;
2417 final ASN1OctetString[] subAny;
2418 final ASN1OctetString subFinal;
2419 final String matchingRuleID;
2420 final boolean dnAttributes;
2421
2422 switch (filterType)
2423 {
2424 case FILTER_TYPE_AND:
2425 case FILTER_TYPE_OR:
2426 notComp = null;
2427 attrName = null;
2428 assertionValue = null;
2429 subInitial = null;
2430 subAny = NO_SUB_ANY;
2431 subFinal = null;
2432 matchingRuleID = null;
2433 dnAttributes = false;
2434
2435 final ASN1Set compSet;
2436 try
2437 {
2438 compSet = ASN1Set.decodeAsSet(filterElement);
2439 }
2440 catch (final ASN1Exception ae)
2441 {
2442 debugException(ae);
2443 throw new LDAPException(ResultCode.DECODING_ERROR,
2444 ERR_FILTER_CANNOT_DECODE_COMPS.get(getExceptionMessage(ae)), ae);
2445 }
2446
2447 final ASN1Element[] compElements = compSet.elements();
2448 filterComps = new Filter[compElements.length];
2449 for (int i=0; i < compElements.length; i++)
2450 {
2451 filterComps[i] = decode(compElements[i]);
2452 }
2453 break;
2454
2455
2456 case FILTER_TYPE_NOT:
2457 filterComps = NO_FILTERS;
2458 attrName = null;
2459 assertionValue = null;
2460 subInitial = null;
2461 subAny = NO_SUB_ANY;
2462 subFinal = null;
2463 matchingRuleID = null;
2464 dnAttributes = false;
2465
2466 final ASN1Element notFilterElement;
2467 try
2468 {
2469 notFilterElement = ASN1Element.decode(filterElement.getValue());
2470 }
2471 catch (final ASN1Exception ae)
2472 {
2473 debugException(ae);
2474 throw new LDAPException(ResultCode.DECODING_ERROR,
2475 ERR_FILTER_CANNOT_DECODE_NOT_COMP.get(getExceptionMessage(ae)),
2476 ae);
2477 }
2478 notComp = decode(notFilterElement);
2479 break;
2480
2481
2482
2483 case FILTER_TYPE_EQUALITY:
2484 case FILTER_TYPE_GREATER_OR_EQUAL:
2485 case FILTER_TYPE_LESS_OR_EQUAL:
2486 case FILTER_TYPE_APPROXIMATE_MATCH:
2487 filterComps = NO_FILTERS;
2488 notComp = null;
2489 subInitial = null;
2490 subAny = NO_SUB_ANY;
2491 subFinal = null;
2492 matchingRuleID = null;
2493 dnAttributes = false;
2494
2495 final ASN1Sequence avaSequence;
2496 try
2497 {
2498 avaSequence = ASN1Sequence.decodeAsSequence(filterElement);
2499 }
2500 catch (final ASN1Exception ae)
2501 {
2502 debugException(ae);
2503 throw new LDAPException(ResultCode.DECODING_ERROR,
2504 ERR_FILTER_CANNOT_DECODE_AVA.get(getExceptionMessage(ae)), ae);
2505 }
2506
2507 final ASN1Element[] avaElements = avaSequence.elements();
2508 if (avaElements.length != 2)
2509 {
2510 throw new LDAPException(ResultCode.DECODING_ERROR,
2511 ERR_FILTER_INVALID_AVA_ELEMENT_COUNT.get(
2512 avaElements.length));
2513 }
2514
2515 attrName =
2516 ASN1OctetString.decodeAsOctetString(avaElements[0]).stringValue();
2517 assertionValue = ASN1OctetString.decodeAsOctetString(avaElements[1]);
2518 break;
2519
2520
2521 case FILTER_TYPE_SUBSTRING:
2522 filterComps = NO_FILTERS;
2523 notComp = null;
2524 assertionValue = null;
2525 matchingRuleID = null;
2526 dnAttributes = false;
2527
2528 final ASN1Sequence subFilterSequence;
2529 try
2530 {
2531 subFilterSequence = ASN1Sequence.decodeAsSequence(filterElement);
2532 }
2533 catch (final ASN1Exception ae)
2534 {
2535 debugException(ae);
2536 throw new LDAPException(ResultCode.DECODING_ERROR,
2537 ERR_FILTER_CANNOT_DECODE_SUBSTRING.get(getExceptionMessage(ae)),
2538 ae);
2539 }
2540
2541 final ASN1Element[] subFilterElements = subFilterSequence.elements();
2542 if (subFilterElements.length != 2)
2543 {
2544 throw new LDAPException(ResultCode.DECODING_ERROR,
2545 ERR_FILTER_INVALID_SUBSTR_ASSERTION_COUNT.get(
2546 subFilterElements.length));
2547 }
2548
2549 attrName = ASN1OctetString.decodeAsOctetString(
2550 subFilterElements[0]).stringValue();
2551
2552 final ASN1Sequence subSequence;
2553 try
2554 {
2555 subSequence = ASN1Sequence.decodeAsSequence(subFilterElements[1]);
2556 }
2557 catch (ASN1Exception ae)
2558 {
2559 debugException(ae);
2560 throw new LDAPException(ResultCode.DECODING_ERROR,
2561 ERR_FILTER_CANNOT_DECODE_SUBSTRING.get(getExceptionMessage(ae)),
2562 ae);
2563 }
2564
2565 ASN1OctetString tempSubInitial = null;
2566 ASN1OctetString tempSubFinal = null;
2567 final ArrayList<ASN1OctetString> subAnyList =
2568 new ArrayList<ASN1OctetString>(1);
2569
2570 final ASN1Element[] subElements = subSequence.elements();
2571 for (final ASN1Element subElement : subElements)
2572 {
2573 switch (subElement.getType())
2574 {
2575 case SUBSTRING_TYPE_SUBINITIAL:
2576 if (tempSubInitial == null)
2577 {
2578 tempSubInitial =
2579 ASN1OctetString.decodeAsOctetString(subElement);
2580 }
2581 else
2582 {
2583 throw new LDAPException(ResultCode.DECODING_ERROR,
2584 ERR_FILTER_MULTIPLE_SUBINITIAL.get());
2585 }
2586 break;
2587
2588 case SUBSTRING_TYPE_SUBANY:
2589 subAnyList.add(ASN1OctetString.decodeAsOctetString(subElement));
2590 break;
2591
2592 case SUBSTRING_TYPE_SUBFINAL:
2593 if (tempSubFinal == null)
2594 {
2595 tempSubFinal = ASN1OctetString.decodeAsOctetString(subElement);
2596 }
2597 else
2598 {
2599 throw new LDAPException(ResultCode.DECODING_ERROR,
2600 ERR_FILTER_MULTIPLE_SUBFINAL.get());
2601 }
2602 break;
2603
2604 default:
2605 throw new LDAPException(ResultCode.DECODING_ERROR,
2606 ERR_FILTER_INVALID_SUBSTR_TYPE.get(
2607 toHex(subElement.getType())));
2608 }
2609 }
2610
2611 subInitial = tempSubInitial;
2612 subAny = subAnyList.toArray(new ASN1OctetString[subAnyList.size()]);
2613 subFinal = tempSubFinal;
2614 break;
2615
2616
2617 case FILTER_TYPE_PRESENCE:
2618 filterComps = NO_FILTERS;
2619 notComp = null;
2620 assertionValue = null;
2621 subInitial = null;
2622 subAny = NO_SUB_ANY;
2623 subFinal = null;
2624 matchingRuleID = null;
2625 dnAttributes = false;
2626 attrName =
2627 ASN1OctetString.decodeAsOctetString(filterElement).stringValue();
2628 break;
2629
2630
2631 case FILTER_TYPE_EXTENSIBLE_MATCH:
2632 filterComps = NO_FILTERS;
2633 notComp = null;
2634 subInitial = null;
2635 subAny = NO_SUB_ANY;
2636 subFinal = null;
2637
2638 final ASN1Sequence emSequence;
2639 try
2640 {
2641 emSequence = ASN1Sequence.decodeAsSequence(filterElement);
2642 }
2643 catch (ASN1Exception ae)
2644 {
2645 debugException(ae);
2646 throw new LDAPException(ResultCode.DECODING_ERROR,
2647 ERR_FILTER_CANNOT_DECODE_EXTMATCH.get(getExceptionMessage(ae)),
2648 ae);
2649 }
2650
2651 String tempAttrName = null;
2652 ASN1OctetString tempAssertionValue = null;
2653 String tempMatchingRuleID = null;
2654 boolean tempDNAttributes = false;
2655 for (final ASN1Element e : emSequence.elements())
2656 {
2657 switch (e.getType())
2658 {
2659 case EXTENSIBLE_TYPE_ATTRIBUTE_NAME:
2660 if (tempAttrName == null)
2661 {
2662 tempAttrName =
2663 ASN1OctetString.decodeAsOctetString(e).stringValue();
2664 }
2665 else
2666 {
2667 throw new LDAPException(ResultCode.DECODING_ERROR,
2668 ERR_FILTER_EXTMATCH_MULTIPLE_ATTRS.get());
2669 }
2670 break;
2671
2672 case EXTENSIBLE_TYPE_MATCHING_RULE_ID:
2673 if (tempMatchingRuleID == null)
2674 {
2675 tempMatchingRuleID =
2676 ASN1OctetString.decodeAsOctetString(e).stringValue();
2677 }
2678 else
2679 {
2680 throw new LDAPException(ResultCode.DECODING_ERROR,
2681 ERR_FILTER_EXTMATCH_MULTIPLE_MRIDS.get());
2682 }
2683 break;
2684
2685 case EXTENSIBLE_TYPE_MATCH_VALUE:
2686 if (tempAssertionValue == null)
2687 {
2688 tempAssertionValue = ASN1OctetString.decodeAsOctetString(e);
2689 }
2690 else
2691 {
2692 throw new LDAPException(ResultCode.DECODING_ERROR,
2693 ERR_FILTER_EXTMATCH_MULTIPLE_VALUES.get());
2694 }
2695 break;
2696
2697 case EXTENSIBLE_TYPE_DN_ATTRIBUTES:
2698 try
2699 {
2700 if (tempDNAttributes)
2701 {
2702 throw new LDAPException(ResultCode.DECODING_ERROR,
2703 ERR_FILTER_EXTMATCH_MULTIPLE_DNATTRS.get());
2704 }
2705 else
2706 {
2707 tempDNAttributes =
2708 ASN1Boolean.decodeAsBoolean(e).booleanValue();
2709 }
2710 }
2711 catch (ASN1Exception ae)
2712 {
2713 debugException(ae);
2714 throw new LDAPException(ResultCode.DECODING_ERROR,
2715 ERR_FILTER_EXTMATCH_DNATTRS_NOT_BOOLEAN.get(
2716 getExceptionMessage(ae)),
2717 ae);
2718 }
2719 break;
2720
2721 default:
2722 throw new LDAPException(ResultCode.DECODING_ERROR,
2723 ERR_FILTER_EXTMATCH_INVALID_TYPE.get(
2724 toHex(e.getType())));
2725 }
2726 }
2727
2728 if ((tempAttrName == null) && (tempMatchingRuleID == null))
2729 {
2730 throw new LDAPException(ResultCode.DECODING_ERROR,
2731 ERR_FILTER_EXTMATCH_NO_ATTR_OR_MRID.get());
2732 }
2733
2734 if (tempAssertionValue == null)
2735 {
2736 throw new LDAPException(ResultCode.DECODING_ERROR,
2737 ERR_FILTER_EXTMATCH_NO_VALUE.get());
2738 }
2739
2740 attrName = tempAttrName;
2741 assertionValue = tempAssertionValue;
2742 matchingRuleID = tempMatchingRuleID;
2743 dnAttributes = tempDNAttributes;
2744 break;
2745
2746
2747 default:
2748 throw new LDAPException(ResultCode.DECODING_ERROR,
2749 ERR_FILTER_ELEMENT_INVALID_TYPE.get(
2750 toHex(filterElement.getType())));
2751 }
2752
2753
2754 return new Filter(null, filterType, filterComps, notComp, attrName,
2755 assertionValue, subInitial, subAny, subFinal,
2756 matchingRuleID, dnAttributes);
2757 }
2758
2759
2760
2761 /**
2762 * Retrieves the filter type for this filter.
2763 *
2764 * @return The filter type for this filter.
2765 */
2766 public byte getFilterType()
2767 {
2768 return filterType;
2769 }
2770
2771
2772
2773 /**
2774 * Retrieves the set of filter components used in this AND or OR filter. This
2775 * is not applicable for any other filter type.
2776 *
2777 * @return The set of filter components used in this AND or OR filter, or an
2778 * empty array if this is some other type of filter or if there are
2779 * no components (i.e., as in an LDAP TRUE or LDAP FALSE filter).
2780 */
2781 public Filter[] getComponents()
2782 {
2783 return filterComps;
2784 }
2785
2786
2787
2788 /**
2789 * Retrieves the filter component used in this NOT filter. This is not
2790 * applicable for any other filter type.
2791 *
2792 * @return The filter component used in this NOT filter, or {@code null} if
2793 * this is some other type of filter.
2794 */
2795 public Filter getNOTComponent()
2796 {
2797 return notComp;
2798 }
2799
2800
2801
2802 /**
2803 * Retrieves the name of the attribute type for this search filter. This is
2804 * applicable for the following types of filters:
2805 * <UL>
2806 * <LI>Equality</LI>
2807 * <LI>Substring</LI>
2808 * <LI>Greater or Equal</LI>
2809 * <LI>Less or Equal</LI>
2810 * <LI>Presence</LI>
2811 * <LI>Approximate Match</LI>
2812 * <LI>Extensible Match</LI>
2813 * </UL>
2814 *
2815 * @return The name of the attribute type for this search filter, or
2816 * {@code null} if it is not applicable for this type of filter.
2817 */
2818 public String getAttributeName()
2819 {
2820 return attrName;
2821 }
2822
2823
2824
2825 /**
2826 * Retrieves the string representation of the assertion value for this search
2827 * filter. This is applicable for the following types of filters:
2828 * <UL>
2829 * <LI>Equality</LI>
2830 * <LI>Greater or Equal</LI>
2831 * <LI>Less or Equal</LI>
2832 * <LI>Approximate Match</LI>
2833 * <LI>Extensible Match</LI>
2834 * </UL>
2835 *
2836 * @return The string representation of the assertion value for this search
2837 * filter, or {@code null} if it is not applicable for this type of
2838 * filter.
2839 */
2840 public String getAssertionValue()
2841 {
2842 if (assertionValue == null)
2843 {
2844 return null;
2845 }
2846 else
2847 {
2848 return assertionValue.stringValue();
2849 }
2850 }
2851
2852
2853
2854 /**
2855 * Retrieves the binary representation of the assertion value for this search
2856 * filter. This is applicable for the following types of filters:
2857 * <UL>
2858 * <LI>Equality</LI>
2859 * <LI>Greater or Equal</LI>
2860 * <LI>Less or Equal</LI>
2861 * <LI>Approximate Match</LI>
2862 * <LI>Extensible Match</LI>
2863 * </UL>
2864 *
2865 * @return The binary representation of the assertion value for this search
2866 * filter, or {@code null} if it is not applicable for this type of
2867 * filter.
2868 */
2869 public byte[] getAssertionValueBytes()
2870 {
2871 if (assertionValue == null)
2872 {
2873 return null;
2874 }
2875 else
2876 {
2877 return assertionValue.getValue();
2878 }
2879 }
2880
2881
2882
2883 /**
2884 * Retrieves the raw assertion value for this search filter as an ASN.1
2885 * octet string. This is applicable for the following types of filters:
2886 * <UL>
2887 * <LI>Equality</LI>
2888 * <LI>Greater or Equal</LI>
2889 * <LI>Less or Equal</LI>
2890 * <LI>Approximate Match</LI>
2891 * <LI>Extensible Match</LI>
2892 * </UL>
2893 *
2894 * @return The raw assertion value for this search filter as an ASN.1 octet
2895 * string, or {@code null} if it is not applicable for this type of
2896 * filter.
2897 */
2898 public ASN1OctetString getRawAssertionValue()
2899 {
2900 return assertionValue;
2901 }
2902
2903
2904
2905 /**
2906 * Retrieves the string representation of the subInitial element for this
2907 * substring filter. This is not applicable for any other filter type.
2908 *
2909 * @return The string representation of the subInitial element for this
2910 * substring filter, or {@code null} if this is some other type of
2911 * filter, or if it is a substring filter with no subInitial element.
2912 */
2913 public String getSubInitialString()
2914 {
2915 if (subInitial == null)
2916 {
2917 return null;
2918 }
2919 else
2920 {
2921 return subInitial.stringValue();
2922 }
2923 }
2924
2925
2926
2927 /**
2928 * Retrieves the binary representation of the subInitial element for this
2929 * substring filter. This is not applicable for any other filter type.
2930 *
2931 * @return The binary representation of the subInitial element for this
2932 * substring filter, or {@code null} if this is some other type of
2933 * filter, or if it is a substring filter with no subInitial element.
2934 */
2935 public byte[] getSubInitialBytes()
2936 {
2937 if (subInitial == null)
2938 {
2939 return null;
2940 }
2941 else
2942 {
2943 return subInitial.getValue();
2944 }
2945 }
2946
2947
2948
2949 /**
2950 * Retrieves the raw subInitial element for this filter as an ASN.1 octet
2951 * string. This is not applicable for any other filter type.
2952 *
2953 * @return The raw subInitial element for this filter as an ASN.1 octet
2954 * string, or {@code null} if this is not a substring filter, or if
2955 * it is a substring filter with no subInitial element.
2956 */
2957 public ASN1OctetString getRawSubInitialValue()
2958 {
2959 return subInitial;
2960 }
2961
2962
2963
2964 /**
2965 * Retrieves the string representations of the subAny elements for this
2966 * substring filter. This is not applicable for any other filter type.
2967 *
2968 * @return The string representations of the subAny elements for this
2969 * substring filter, or an empty array if this is some other type of
2970 * filter, or if it is a substring filter with no subFinal element.
2971 */
2972 public String[] getSubAnyStrings()
2973 {
2974 final String[] subAnyStrings = new String[subAny.length];
2975 for (int i=0; i < subAny.length; i++)
2976 {
2977 subAnyStrings[i] = subAny[i].stringValue();
2978 }
2979
2980 return subAnyStrings;
2981 }
2982
2983
2984
2985 /**
2986 * Retrieves the binary representations of the subAny elements for this
2987 * substring filter. This is not applicable for any other filter type.
2988 *
2989 * @return The binary representations of the subAny elements for this
2990 * substring filter, or an empty array if this is some other type of
2991 * filter, or if it is a substring filter with no subFinal element.
2992 */
2993 public byte[][] getSubAnyBytes()
2994 {
2995 final byte[][] subAnyBytes = new byte[subAny.length][];
2996 for (int i=0; i < subAny.length; i++)
2997 {
2998 subAnyBytes[i] = subAny[i].getValue();
2999 }
3000
3001 return subAnyBytes;
3002 }
3003
3004
3005
3006 /**
3007 * Retrieves the raw subAny values for this substring filter. This is not
3008 * applicable for any other filter type.
3009 *
3010 * @return The raw subAny values for this substring filter, or an empty array
3011 * if this is some other type of filter, or if it is a substring
3012 * filter with no subFinal element.
3013 */
3014 public ASN1OctetString[] getRawSubAnyValues()
3015 {
3016 return subAny;
3017 }
3018
3019
3020
3021 /**
3022 * Retrieves the string representation of the subFinal element for this
3023 * substring filter. This is not applicable for any other filter type.
3024 *
3025 * @return The string representation of the subFinal element for this
3026 * substring filter, or {@code null} if this is some other type of
3027 * filter, or if it is a substring filter with no subFinal element.
3028 */
3029 public String getSubFinalString()
3030 {
3031 if (subFinal == null)
3032 {
3033 return null;
3034 }
3035 else
3036 {
3037 return subFinal.stringValue();
3038 }
3039 }
3040
3041
3042
3043 /**
3044 * Retrieves the binary representation of the subFinal element for this
3045 * substring filter. This is not applicable for any other filter type.
3046 *
3047 * @return The binary representation of the subFinal element for this
3048 * substring filter, or {@code null} if this is some other type of
3049 * filter, or if it is a substring filter with no subFinal element.
3050 */
3051 public byte[] getSubFinalBytes()
3052 {
3053 if (subFinal == null)
3054 {
3055 return null;
3056 }
3057 else
3058 {
3059 return subFinal.getValue();
3060 }
3061 }
3062
3063
3064
3065 /**
3066 * Retrieves the raw subFinal element for this filter as an ASN.1 octet
3067 * string. This is not applicable for any other filter type.
3068 *
3069 * @return The raw subFinal element for this filter as an ASN.1 octet
3070 * string, or {@code null} if this is not a substring filter, or if
3071 * it is a substring filter with no subFinal element.
3072 */
3073 public ASN1OctetString getRawSubFinalValue()
3074 {
3075 return subFinal;
3076 }
3077
3078
3079
3080 /**
3081 * Retrieves the matching rule ID for this extensible match filter. This is
3082 * not applicable for any other filter type.
3083 *
3084 * @return The matching rule ID for this extensible match filter, or
3085 * {@code null} if this is some other type of filter, or if this
3086 * extensible match filter does not have a matching rule ID.
3087 */
3088 public String getMatchingRuleID()
3089 {
3090 return matchingRuleID;
3091 }
3092
3093
3094
3095 /**
3096 * Retrieves the dnAttributes flag for this extensible match filter. This is
3097 * not applicable for any other filter type.
3098 *
3099 * @return The dnAttributes flag for this extensible match filter.
3100 */
3101 public boolean getDNAttributes()
3102 {
3103 return dnAttributes;
3104 }
3105
3106
3107
3108 /**
3109 * Indicates whether this filter matches the provided entry. Note that this
3110 * is a best-guess effort and may not be completely accurate in all cases.
3111 * All matching will be performed using case-ignore string matching, which may
3112 * yield an unexpected result for values that should not be treated as simple
3113 * strings. For example:
3114 * <UL>
3115 * <LI>Two DN values which are logically equivalent may not be considered
3116 * matches if they have different spacing.</LI>
3117 * <LI>Ordering comparisons against numeric values may yield unexpected
3118 * results (e.g., "2" will be considered greater than "10" because the
3119 * character "2" has a larger ASCII value than the character "1").</LI>
3120 * </UL>
3121 * <BR>
3122 * In addition to the above constraints, it should be noted that neither
3123 * approximate matching nor extensible matching are currently supported.
3124 *
3125 * @param entry The entry for which to make the determination. It must not
3126 * be {@code null}.
3127 *
3128 * @return {@code true} if this filter appears to match the provided entry,
3129 * or {@code false} if not.
3130 *
3131 * @throws LDAPException If a problem occurs while trying to make the
3132 * determination.
3133 */
3134 public boolean matchesEntry(final Entry entry)
3135 throws LDAPException
3136 {
3137 return matchesEntry(entry, entry.getSchema());
3138 }
3139
3140
3141
3142 /**
3143 * Indicates whether this filter matches the provided entry. Note that this
3144 * is a best-guess effort and may not be completely accurate in all cases.
3145 * If provided, the given schema will be used in an attempt to determine the
3146 * appropriate matching rule for making the determinations, but some corner
3147 * cases may not be handled accurately. Neither approximate matching nor
3148 * extensible matching are currently supported.
3149 *
3150 * @param entry The entry for which to make the determination. It must not
3151 * be {@code null}.
3152 * @param schema The schema to use when making the determination. If this
3153 * is {@code null}, then all matching will be performed using
3154 * a case-ignore matching rule.
3155 *
3156 * @return {@code true} if this filter appears to match the provided entry,
3157 * or {@code false} if not.
3158 *
3159 * @throws LDAPException If a problem occurs while trying to make the
3160 * determination.
3161 */
3162 public boolean matchesEntry(final Entry entry, final Schema schema)
3163 throws LDAPException
3164 {
3165 ensureNotNull(entry);
3166
3167 switch (filterType)
3168 {
3169 case FILTER_TYPE_AND:
3170 for (final Filter f : filterComps)
3171 {
3172 if (! f.matchesEntry(entry, schema))
3173 {
3174 return false;
3175 }
3176 }
3177 return true;
3178
3179 case FILTER_TYPE_OR:
3180 for (final Filter f : filterComps)
3181 {
3182 if (f.matchesEntry(entry, schema))
3183 {
3184 return true;
3185 }
3186 }
3187 return false;
3188
3189 case FILTER_TYPE_NOT:
3190 return (! notComp.matchesEntry(entry, schema));
3191
3192 case FILTER_TYPE_EQUALITY:
3193 Attribute a = entry.getAttribute(attrName, schema);
3194 if (a == null)
3195 {
3196 return false;
3197 }
3198
3199 MatchingRule matchingRule =
3200 MatchingRule.selectEqualityMatchingRule(attrName, schema);
3201 for (final ASN1OctetString v : a.getRawValues())
3202 {
3203 if (matchingRule.valuesMatch(v, assertionValue))
3204 {
3205 return true;
3206 }
3207 }
3208 return false;
3209
3210 case FILTER_TYPE_SUBSTRING:
3211 a = entry.getAttribute(attrName, schema);
3212 if (a == null)
3213 {
3214 return false;
3215 }
3216
3217 matchingRule =
3218 MatchingRule.selectSubstringMatchingRule(attrName, schema);
3219 for (final ASN1OctetString v : a.getRawValues())
3220 {
3221 if (matchingRule.matchesSubstring(v, subInitial, subAny, subFinal))
3222 {
3223 return true;
3224 }
3225 }
3226 return false;
3227
3228 case FILTER_TYPE_GREATER_OR_EQUAL:
3229 a = entry.getAttribute(attrName, schema);
3230 if (a == null)
3231 {
3232 return false;
3233 }
3234
3235 matchingRule =
3236 MatchingRule.selectOrderingMatchingRule(attrName, schema);
3237 for (final ASN1OctetString v : a.getRawValues())
3238 {
3239 if (matchingRule.compareValues(v, assertionValue) >= 0)
3240 {
3241 return true;
3242 }
3243 }
3244 return false;
3245
3246 case FILTER_TYPE_LESS_OR_EQUAL:
3247 a = entry.getAttribute(attrName, schema);
3248 if (a == null)
3249 {
3250 return false;
3251 }
3252
3253 matchingRule =
3254 MatchingRule.selectOrderingMatchingRule(attrName, schema);
3255 for (final ASN1OctetString v : a.getRawValues())
3256 {
3257 if (matchingRule.compareValues(v, assertionValue) <= 0)
3258 {
3259 return true;
3260 }
3261 }
3262 return false;
3263
3264 case FILTER_TYPE_PRESENCE:
3265 return (entry.hasAttribute(attrName));
3266
3267 case FILTER_TYPE_APPROXIMATE_MATCH:
3268 throw new LDAPException(ResultCode.NOT_SUPPORTED,
3269 ERR_FILTER_APPROXIMATE_MATCHING_NOT_SUPPORTED.get());
3270
3271 case FILTER_TYPE_EXTENSIBLE_MATCH:
3272 throw new LDAPException(ResultCode.NOT_SUPPORTED,
3273 ERR_FILTER_EXTENSIBLE_MATCHING_NOT_SUPPORTED.get());
3274
3275 default:
3276 throw new LDAPException(ResultCode.PARAM_ERROR,
3277 ERR_FILTER_INVALID_TYPE.get());
3278 }
3279 }
3280
3281
3282
3283 /**
3284 * Attempts to simplify the provided filter to allow it to be more efficiently
3285 * processed by the server. The simplifications it will make include:
3286 * <UL>
3287 * <LI>Any AND or OR filter that contains only a single filter component
3288 * will be converted to just that embedded filter component to eliminate
3289 * the unnecessary AND or OR wrapper. For example, the filter
3290 * "(&(uid=john.doe))" will be converted to just "(uid=john.doe)".</LI>
3291 * <LI>Any AND components inside of an AND filter will be merged into the
3292 * outer AND filter. Any OR components inside of an OR filter will be
3293 * merged into the outer OR filter. For example, the filter
3294 * "(&(objectClass=person)(&(givenName=John)(sn=Doe)))" will be
3295 * converted to "(&(objectClass=person)(givenName=John)(sn=Doe))".</LI>
3296 * <LI>If {@code reOrderElements} is true, then this method will attempt to
3297 * re-order the elements inside AND and OR filters in an attempt to
3298 * ensure that the components which are likely to be the most efficient
3299 * come earlier than those which are likely to be the least efficient.
3300 * This can speed up processing in servers that process filter
3301 * components in a left-to-right order.</LI>
3302 * </UL>
3303 * <BR><BR>
3304 * The simplification will happen recursively, in an attempt to generate a
3305 * filter that is as simple and efficient as possible.
3306 *
3307 * @param filter The filter to attempt to simplify.
3308 * @param reOrderElements Indicates whether this method may re-order the
3309 * elements in the filter so that, in a server that
3310 * evaluates the components in a left-to-right order,
3311 * the components which are likely to be more
3312 * efficient to process will be listed before those
3313 * which are likely to be less efficient.
3314 *
3315 * @return The simplified filter, or the original filter if the provided
3316 * filter is not one that can be simplified any further.
3317 */
3318 public static Filter simplifyFilter(final Filter filter,
3319 final boolean reOrderElements)
3320 {
3321 final byte filterType = filter.filterType;
3322 switch (filterType)
3323 {
3324 case FILTER_TYPE_AND:
3325 case FILTER_TYPE_OR:
3326 // These will be handled below.
3327 break;
3328
3329 case FILTER_TYPE_NOT:
3330 // We may be able to simplify the filter component contained inside the
3331 // NOT.
3332 return createNOTFilter(simplifyFilter(filter.notComp, reOrderElements));
3333
3334 default:
3335 // We can't simplify this filter, so just return what was provided.
3336 return filter;
3337 }
3338
3339
3340 // An AND filter with zero components is an LDAP true filter, and we can't
3341 // simplify that. An OR filter with zero components is an LDAP false
3342 // filter, and we can't simplify that either. The set of components
3343 // should never be null for an AND or OR filter, but if that happens to be
3344 // the case, then we'll return the original filter.
3345 final Filter[] components = filter.filterComps;
3346 if ((components == null) || (components.length == 0))
3347 {
3348 return filter;
3349 }
3350
3351
3352 // For either an AND or an OR filter with just a single component, then just
3353 // return that embedded component. But simplify it first.
3354 if (components.length == 1)
3355 {
3356 return simplifyFilter(components[0], reOrderElements);
3357 }
3358
3359
3360 // If we've gotten here, then we have a filter with multiple components.
3361 // Simplify each of them to the extent possible, un-embed any ANDs
3362 // contained inside an AND or ORs contained inside an OR, and eliminate any
3363 // duplicate components in the resulting top-level filter.
3364 final LinkedHashSet<Filter> componentSet = new LinkedHashSet<Filter>(10);
3365 for (final Filter f : components)
3366 {
3367 final Filter simplifiedFilter = simplifyFilter(f, reOrderElements);
3368 if (simplifiedFilter.filterType == FILTER_TYPE_AND)
3369 {
3370 if (filterType == FILTER_TYPE_AND)
3371 {
3372 // This is an AND nested inside an AND. In that case, we'll just put
3373 // all the nested components inside the outer AND.
3374 componentSet.addAll(Arrays.asList(simplifiedFilter.filterComps));
3375 }
3376 else
3377 {
3378 componentSet.add(simplifiedFilter);
3379 }
3380 }
3381 else if (simplifiedFilter.filterType == FILTER_TYPE_OR)
3382 {
3383 if (filterType == FILTER_TYPE_OR)
3384 {
3385 // This is an OR nested inside an OR. In that case, we'll just put
3386 // all the nested components inside the outer OR.
3387 componentSet.addAll(Arrays.asList(simplifiedFilter.filterComps));
3388 }
3389 else
3390 {
3391 componentSet.add(simplifiedFilter);
3392 }
3393 }
3394 else
3395 {
3396 componentSet.add(simplifiedFilter);
3397 }
3398 }
3399
3400
3401 // It's possible at this point that we are down to just a single component.
3402 // That can happen if the filter was an AND or an OR with a duplicate
3403 // element, like "(&(a=b)(a=b))". In that case, just return that one
3404 // component.
3405 if (componentSet.size() == 1)
3406 {
3407 return componentSet.iterator().next();
3408 }
3409
3410
3411 // If we should re-order the components, then use the following priority
3412 // list:
3413 //
3414 // 1. Equality components that target an attribute other than objectClass.
3415 // These are most likely to require only a single database lookup to get
3416 // the candidate list, and that candidate list will frequently be small.
3417 // 2. Equality components that target the objectClass attribute. These are
3418 // likely to require only a single database lookup to get the candidate
3419 // list, but the candidate list is more likely to be larger.
3420 // 3. Approximate match components. These are also likely to require only
3421 // a single database lookup to get the candidate list, but that
3422 // candidate list is likely to have a larger number of candidates.
3423 // 4. Presence components that target an attribute other than objectClass.
3424 // These are also likely to require only a single database lookup to get
3425 // the candidate list, but are likely to have a large number of
3426 // candidates.
3427 // 5. Substring components that have a subInitial element. These are
3428 // generally the most efficient substring filters to process, requiring
3429 // access to fewer database keys than substring filters with only subAny
3430 // and/or subFinal components.
3431 // 6. Substring components that only have subAny and/or subFinal elements.
3432 // These will probably require a number of database lookups and will
3433 // probably result in large candidate lists.
3434 // 7. Greater-or-equal components and less-or-equal components. These
3435 // will probably require a number of database lookups and will probably
3436 // result in large candidate lists.
3437 // 8. Extensible match components. Even if these are indexed, there isn't
3438 // any good way to know how expensive they might be to process or how
3439 // big the candidate list might be.
3440 // 9. Presence components that target the objectClass attribute. This is
3441 // likely to require only a single database lookup to get the candidate
3442 // list, but the candidate list will also be extremely large (if it's
3443 // indexed at all) since it will match every entry.
3444 // 10. NOT components. These are generally not possible to index and
3445 // therefore cannot be used to create a candidate list.
3446 //
3447 // AND and OR components will be ordered according to the first of their
3448 // embedded components Since the filter has already been simplified, then
3449 // the first element in the list will be the one we think will be the most
3450 // efficient to process.
3451 if (reOrderElements)
3452 {
3453 final TreeMap<Integer,LinkedHashSet<Filter>> m =
3454 new TreeMap<Integer,LinkedHashSet<Filter>>();
3455 for (final Filter f : componentSet)
3456 {
3457 final Filter prioritizeComp;
3458 if ((f.filterType == FILTER_TYPE_AND) ||
3459 (f.filterType == FILTER_TYPE_OR))
3460 {
3461 if (f.filterComps.length > 0)
3462 {
3463 prioritizeComp = f.filterComps[0];
3464 }
3465 else
3466 {
3467 prioritizeComp = f;
3468 }
3469 }
3470 else
3471 {
3472 prioritizeComp = f;
3473 }
3474
3475 final Integer slot;
3476 switch (prioritizeComp.filterType)
3477 {
3478 case FILTER_TYPE_EQUALITY:
3479 if (prioritizeComp.attrName.equalsIgnoreCase("objectClass"))
3480 {
3481 slot = 2;
3482 }
3483 else
3484 {
3485 slot = 1;
3486 }
3487 break;
3488
3489 case FILTER_TYPE_APPROXIMATE_MATCH:
3490 slot = 3;
3491 break;
3492
3493 case FILTER_TYPE_PRESENCE:
3494 if (prioritizeComp.attrName.equalsIgnoreCase("objectClass"))
3495 {
3496 slot = 9;
3497 }
3498 else
3499 {
3500 slot = 4;
3501 }
3502 break;
3503
3504 case FILTER_TYPE_SUBSTRING:
3505 if (prioritizeComp.subInitial == null)
3506 {
3507 slot = 6;
3508 }
3509 else
3510 {
3511 slot = 5;
3512 }
3513 break;
3514
3515 case FILTER_TYPE_GREATER_OR_EQUAL:
3516 case FILTER_TYPE_LESS_OR_EQUAL:
3517 slot = 7;
3518 break;
3519
3520 case FILTER_TYPE_EXTENSIBLE_MATCH:
3521 slot = 8;
3522 break;
3523
3524 case FILTER_TYPE_NOT:
3525 default:
3526 slot = 10;
3527 break;
3528 }
3529
3530 LinkedHashSet<Filter> filterSet = m.get(slot-1);
3531 if (filterSet == null)
3532 {
3533 filterSet = new LinkedHashSet<Filter>(10);
3534 m.put(slot-1, filterSet);
3535 }
3536 filterSet.add(f);
3537 }
3538
3539 componentSet.clear();
3540 for (final LinkedHashSet<Filter> filterSet : m.values())
3541 {
3542 componentSet.addAll(filterSet);
3543 }
3544 }
3545
3546
3547 // Return the new, possibly simplified filter.
3548 if (filterType == FILTER_TYPE_AND)
3549 {
3550 return createANDFilter(componentSet);
3551 }
3552 else
3553 {
3554 return createORFilter(componentSet);
3555 }
3556 }
3557
3558
3559
3560 /**
3561 * Generates a hash code for this search filter.
3562 *
3563 * @return The generated hash code for this search filter.
3564 */
3565 @Override()
3566 public int hashCode()
3567 {
3568 final CaseIgnoreStringMatchingRule matchingRule =
3569 CaseIgnoreStringMatchingRule.getInstance();
3570 int hashCode = filterType;
3571
3572 switch (filterType)
3573 {
3574 case FILTER_TYPE_AND:
3575 case FILTER_TYPE_OR:
3576 for (final Filter f : filterComps)
3577 {
3578 hashCode += f.hashCode();
3579 }
3580 break;
3581
3582 case FILTER_TYPE_NOT:
3583 hashCode += notComp.hashCode();
3584 break;
3585
3586 case FILTER_TYPE_EQUALITY:
3587 case FILTER_TYPE_GREATER_OR_EQUAL:
3588 case FILTER_TYPE_LESS_OR_EQUAL:
3589 case FILTER_TYPE_APPROXIMATE_MATCH:
3590 hashCode += toLowerCase(attrName).hashCode();
3591 hashCode += matchingRule.normalize(assertionValue).hashCode();
3592 break;
3593
3594 case FILTER_TYPE_SUBSTRING:
3595 hashCode += toLowerCase(attrName).hashCode();
3596 if (subInitial != null)
3597 {
3598 hashCode += matchingRule.normalizeSubstring(subInitial,
3599 MatchingRule.SUBSTRING_TYPE_SUBINITIAL).hashCode();
3600 }
3601 for (final ASN1OctetString s : subAny)
3602 {
3603 hashCode += matchingRule.normalizeSubstring(s,
3604 MatchingRule.SUBSTRING_TYPE_SUBANY).hashCode();
3605 }
3606 if (subFinal != null)
3607 {
3608 hashCode += matchingRule.normalizeSubstring(subFinal,
3609 MatchingRule.SUBSTRING_TYPE_SUBFINAL).hashCode();
3610 }
3611 break;
3612
3613 case FILTER_TYPE_PRESENCE:
3614 hashCode += toLowerCase(attrName).hashCode();
3615 break;
3616
3617 case FILTER_TYPE_EXTENSIBLE_MATCH:
3618 if (attrName != null)
3619 {
3620 hashCode += toLowerCase(attrName).hashCode();
3621 }
3622
3623 if (matchingRuleID != null)
3624 {
3625 hashCode += toLowerCase(matchingRuleID).hashCode();
3626 }
3627
3628 if (dnAttributes)
3629 {
3630 hashCode++;
3631 }
3632
3633 hashCode += matchingRule.normalize(assertionValue).hashCode();
3634 break;
3635 }
3636
3637 return hashCode;
3638 }
3639
3640
3641
3642 /**
3643 * Indicates whether the provided object is equal to this search filter.
3644 *
3645 * @param o The object for which to make the determination.
3646 *
3647 * @return {@code true} if the provided object can be considered equal to
3648 * this search filter, or {@code false} if not.
3649 */
3650 @Override()
3651 public boolean equals(final Object o)
3652 {
3653 if (o == null)
3654 {
3655 return false;
3656 }
3657
3658 if (o == this)
3659 {
3660 return true;
3661 }
3662
3663 if (! (o instanceof Filter))
3664 {
3665 return false;
3666 }
3667
3668 final Filter f = (Filter) o;
3669 if (filterType != f.filterType)
3670 {
3671 return false;
3672 }
3673
3674 final CaseIgnoreStringMatchingRule matchingRule =
3675 CaseIgnoreStringMatchingRule.getInstance();
3676
3677 switch (filterType)
3678 {
3679 case FILTER_TYPE_AND:
3680 case FILTER_TYPE_OR:
3681 if (filterComps.length != f.filterComps.length)
3682 {
3683 return false;
3684 }
3685
3686 final HashSet<Filter> compSet = new HashSet<Filter>();
3687 compSet.addAll(Arrays.asList(filterComps));
3688
3689 for (final Filter filterComp : f.filterComps)
3690 {
3691 if (! compSet.remove(filterComp))
3692 {
3693 return false;
3694 }
3695 }
3696
3697 return true;
3698
3699
3700 case FILTER_TYPE_NOT:
3701 return notComp.equals(f.notComp);
3702
3703
3704 case FILTER_TYPE_EQUALITY:
3705 case FILTER_TYPE_GREATER_OR_EQUAL:
3706 case FILTER_TYPE_LESS_OR_EQUAL:
3707 case FILTER_TYPE_APPROXIMATE_MATCH:
3708 return (attrName.equalsIgnoreCase(f.attrName) &&
3709 matchingRule.valuesMatch(assertionValue, f.assertionValue));
3710
3711
3712 case FILTER_TYPE_SUBSTRING:
3713 if (! attrName.equalsIgnoreCase(f.attrName))
3714 {
3715 return false;
3716 }
3717
3718 if (subAny.length != f.subAny.length)
3719 {
3720 return false;
3721 }
3722
3723 if (subInitial == null)
3724 {
3725 if (f.subInitial != null)
3726 {
3727 return false;
3728 }
3729 }
3730 else
3731 {
3732 if (f.subInitial == null)
3733 {
3734 return false;
3735 }
3736
3737 final ASN1OctetString si1 = matchingRule.normalizeSubstring(
3738 subInitial, MatchingRule.SUBSTRING_TYPE_SUBINITIAL);
3739 final ASN1OctetString si2 = matchingRule.normalizeSubstring(
3740 f.subInitial, MatchingRule.SUBSTRING_TYPE_SUBINITIAL);
3741 if (! si1.equals(si2))
3742 {
3743 return false;
3744 }
3745 }
3746
3747 for (int i=0; i < subAny.length; i++)
3748 {
3749 final ASN1OctetString sa1 = matchingRule.normalizeSubstring(subAny[i],
3750 MatchingRule.SUBSTRING_TYPE_SUBANY);
3751 final ASN1OctetString sa2 = matchingRule.normalizeSubstring(
3752 f.subAny[i], MatchingRule.SUBSTRING_TYPE_SUBANY);
3753 if (! sa1.equals(sa2))
3754 {
3755 return false;
3756 }
3757 }
3758
3759 if (subFinal == null)
3760 {
3761 if (f.subFinal != null)
3762 {
3763 return false;
3764 }
3765 }
3766 else
3767 {
3768 if (f.subFinal == null)
3769 {
3770 return false;
3771 }
3772
3773 final ASN1OctetString sf1 = matchingRule.normalizeSubstring(subFinal,
3774 MatchingRule.SUBSTRING_TYPE_SUBFINAL);
3775 final ASN1OctetString sf2 = matchingRule.normalizeSubstring(
3776 f.subFinal, MatchingRule.SUBSTRING_TYPE_SUBFINAL);
3777 if (! sf1.equals(sf2))
3778 {
3779 return false;
3780 }
3781 }
3782
3783 return true;
3784
3785
3786 case FILTER_TYPE_PRESENCE:
3787 return (attrName.equalsIgnoreCase(f.attrName));
3788
3789
3790 case FILTER_TYPE_EXTENSIBLE_MATCH:
3791 if (attrName == null)
3792 {
3793 if (f.attrName != null)
3794 {
3795 return false;
3796 }
3797 }
3798 else
3799 {
3800 if (f.attrName == null)
3801 {
3802 return false;
3803 }
3804 else
3805 {
3806 if (! attrName.equalsIgnoreCase(f.attrName))
3807 {
3808 return false;
3809 }
3810 }
3811 }
3812
3813 if (matchingRuleID == null)
3814 {
3815 if (f.matchingRuleID != null)
3816 {
3817 return false;
3818 }
3819 }
3820 else
3821 {
3822 if (f.matchingRuleID == null)
3823 {
3824 return false;
3825 }
3826 else
3827 {
3828 if (! matchingRuleID.equalsIgnoreCase(f.matchingRuleID))
3829 {
3830 return false;
3831 }
3832 }
3833 }
3834
3835 if (dnAttributes != f.dnAttributes)
3836 {
3837 return false;
3838 }
3839
3840 return matchingRule.valuesMatch(assertionValue, f.assertionValue);
3841
3842
3843 default:
3844 return false;
3845 }
3846 }
3847
3848
3849
3850 /**
3851 * Retrieves a string representation of this search filter.
3852 *
3853 * @return A string representation of this search filter.
3854 */
3855 @Override()
3856 public String toString()
3857 {
3858 if (filterString == null)
3859 {
3860 final StringBuilder buffer = new StringBuilder();
3861 toString(buffer);
3862 filterString = buffer.toString();
3863 }
3864
3865 return filterString;
3866 }
3867
3868
3869
3870 /**
3871 * Appends a string representation of this search filter to the provided
3872 * buffer.
3873 *
3874 * @param buffer The buffer to which to append a string representation of
3875 * this search filter.
3876 */
3877 public void toString(final StringBuilder buffer)
3878 {
3879 switch (filterType)
3880 {
3881 case FILTER_TYPE_AND:
3882 buffer.append("(&");
3883 for (final Filter f : filterComps)
3884 {
3885 f.toString(buffer);
3886 }
3887 buffer.append(')');
3888 break;
3889
3890 case FILTER_TYPE_OR:
3891 buffer.append("(|");
3892 for (final Filter f : filterComps)
3893 {
3894 f.toString(buffer);
3895 }
3896 buffer.append(')');
3897 break;
3898
3899 case FILTER_TYPE_NOT:
3900 buffer.append("(!");
3901 notComp.toString(buffer);
3902 buffer.append(')');
3903 break;
3904
3905 case FILTER_TYPE_EQUALITY:
3906 buffer.append('(');
3907 buffer.append(attrName);
3908 buffer.append('=');
3909 encodeValue(assertionValue, buffer);
3910 buffer.append(')');
3911 break;
3912
3913 case FILTER_TYPE_SUBSTRING:
3914 buffer.append('(');
3915 buffer.append(attrName);
3916 buffer.append('=');
3917 if (subInitial != null)
3918 {
3919 encodeValue(subInitial, buffer);
3920 }
3921 buffer.append('*');
3922 for (final ASN1OctetString s : subAny)
3923 {
3924 encodeValue(s, buffer);
3925 buffer.append('*');
3926 }
3927 if (subFinal != null)
3928 {
3929 encodeValue(subFinal, buffer);
3930 }
3931 buffer.append(')');
3932 break;
3933
3934 case FILTER_TYPE_GREATER_OR_EQUAL:
3935 buffer.append('(');
3936 buffer.append(attrName);
3937 buffer.append(">=");
3938 encodeValue(assertionValue, buffer);
3939 buffer.append(')');
3940 break;
3941
3942 case FILTER_TYPE_LESS_OR_EQUAL:
3943 buffer.append('(');
3944 buffer.append(attrName);
3945 buffer.append("<=");
3946 encodeValue(assertionValue, buffer);
3947 buffer.append(')');
3948 break;
3949
3950 case FILTER_TYPE_PRESENCE:
3951 buffer.append('(');
3952 buffer.append(attrName);
3953 buffer.append("=*)");
3954 break;
3955
3956 case FILTER_TYPE_APPROXIMATE_MATCH:
3957 buffer.append('(');
3958 buffer.append(attrName);
3959 buffer.append("~=");
3960 encodeValue(assertionValue, buffer);
3961 buffer.append(')');
3962 break;
3963
3964 case FILTER_TYPE_EXTENSIBLE_MATCH:
3965 buffer.append('(');
3966 if (attrName != null)
3967 {
3968 buffer.append(attrName);
3969 }
3970
3971 if (dnAttributes)
3972 {
3973 buffer.append(":dn");
3974 }
3975
3976 if (matchingRuleID != null)
3977 {
3978 buffer.append(':');
3979 buffer.append(matchingRuleID);
3980 }
3981
3982 buffer.append(":=");
3983 encodeValue(assertionValue, buffer);
3984 buffer.append(')');
3985 break;
3986 }
3987 }
3988
3989
3990
3991 /**
3992 * Retrieves a normalized string representation of this search filter.
3993 *
3994 * @return A normalized string representation of this search filter.
3995 */
3996 public String toNormalizedString()
3997 {
3998 if (normalizedString == null)
3999 {
4000 final StringBuilder buffer = new StringBuilder();
4001 toNormalizedString(buffer);
4002 normalizedString = buffer.toString();
4003 }
4004
4005 return normalizedString;
4006 }
4007
4008
4009
4010 /**
4011 * Appends a normalized string representation of this search filter to the
4012 * provided buffer.
4013 *
4014 * @param buffer The buffer to which to append a normalized string
4015 * representation of this search filter.
4016 */
4017 public void toNormalizedString(final StringBuilder buffer)
4018 {
4019 final CaseIgnoreStringMatchingRule mr =
4020 CaseIgnoreStringMatchingRule.getInstance();
4021
4022 switch (filterType)
4023 {
4024 case FILTER_TYPE_AND:
4025 buffer.append("(&");
4026 for (final Filter f : filterComps)
4027 {
4028 f.toNormalizedString(buffer);
4029 }
4030 buffer.append(')');
4031 break;
4032
4033 case FILTER_TYPE_OR:
4034 buffer.append("(|");
4035 for (final Filter f : filterComps)
4036 {
4037 f.toNormalizedString(buffer);
4038 }
4039 buffer.append(')');
4040 break;
4041
4042 case FILTER_TYPE_NOT:
4043 buffer.append("(!");
4044 notComp.toNormalizedString(buffer);
4045 buffer.append(')');
4046 break;
4047
4048 case FILTER_TYPE_EQUALITY:
4049 buffer.append('(');
4050 buffer.append(toLowerCase(attrName));
4051 buffer.append('=');
4052 encodeValue(mr.normalize(assertionValue), buffer);
4053 buffer.append(')');
4054 break;
4055
4056 case FILTER_TYPE_SUBSTRING:
4057 buffer.append('(');
4058 buffer.append(toLowerCase(attrName));
4059 buffer.append('=');
4060 if (subInitial != null)
4061 {
4062 encodeValue(mr.normalizeSubstring(subInitial,
4063 MatchingRule.SUBSTRING_TYPE_SUBINITIAL), buffer);
4064 }
4065 buffer.append('*');
4066 for (final ASN1OctetString s : subAny)
4067 {
4068 encodeValue(mr.normalizeSubstring(s,
4069 MatchingRule.SUBSTRING_TYPE_SUBANY), buffer);
4070 buffer.append('*');
4071 }
4072 if (subFinal != null)
4073 {
4074 encodeValue(mr.normalizeSubstring(subFinal,
4075 MatchingRule.SUBSTRING_TYPE_SUBFINAL), buffer);
4076 }
4077 buffer.append(')');
4078 break;
4079
4080 case FILTER_TYPE_GREATER_OR_EQUAL:
4081 buffer.append('(');
4082 buffer.append(toLowerCase(attrName));
4083 buffer.append(">=");
4084 encodeValue(mr.normalize(assertionValue), buffer);
4085 buffer.append(')');
4086 break;
4087
4088 case FILTER_TYPE_LESS_OR_EQUAL:
4089 buffer.append('(');
4090 buffer.append(toLowerCase(attrName));
4091 buffer.append("<=");
4092 encodeValue(mr.normalize(assertionValue), buffer);
4093 buffer.append(')');
4094 break;
4095
4096 case FILTER_TYPE_PRESENCE:
4097 buffer.append('(');
4098 buffer.append(toLowerCase(attrName));
4099 buffer.append("=*)");
4100 break;
4101
4102 case FILTER_TYPE_APPROXIMATE_MATCH:
4103 buffer.append('(');
4104 buffer.append(toLowerCase(attrName));
4105 buffer.append("~=");
4106 encodeValue(mr.normalize(assertionValue), buffer);
4107 buffer.append(')');
4108 break;
4109
4110 case FILTER_TYPE_EXTENSIBLE_MATCH:
4111 buffer.append('(');
4112 if (attrName != null)
4113 {
4114 buffer.append(toLowerCase(attrName));
4115 }
4116
4117 if (dnAttributes)
4118 {
4119 buffer.append(":dn");
4120 }
4121
4122 if (matchingRuleID != null)
4123 {
4124 buffer.append(':');
4125 buffer.append(toLowerCase(matchingRuleID));
4126 }
4127
4128 buffer.append(":=");
4129 encodeValue(mr.normalize(assertionValue), buffer);
4130 buffer.append(')');
4131 break;
4132 }
4133 }
4134
4135
4136
4137 /**
4138 * Encodes the provided value into a form suitable for use as the assertion
4139 * value in the string representation of a search filter. Parentheses,
4140 * asterisks, backslashes, null characters, and any non-ASCII characters will
4141 * be escaped using a backslash before the hexadecimal representation of each
4142 * byte in the character to escape.
4143 *
4144 * @param value The value to be encoded. It must not be {@code null}.
4145 *
4146 * @return The encoded representation of the provided string.
4147 */
4148 public static String encodeValue(final String value)
4149 {
4150 ensureNotNull(value);
4151
4152 final StringBuilder buffer = new StringBuilder();
4153 encodeValue(new ASN1OctetString(value), buffer);
4154 return buffer.toString();
4155 }
4156
4157
4158
4159 /**
4160 * Encodes the provided value into a form suitable for use as the assertion
4161 * value in the string representation of a search filter. Parentheses,
4162 * asterisks, backslashes, null characters, and any non-ASCII characters will
4163 * be escaped using a backslash before the hexadecimal representation of each
4164 * byte in the character to escape.
4165 *
4166 * @param value The value to be encoded. It must not be {@code null}.
4167 *
4168 * @return The encoded representation of the provided string.
4169 */
4170 public static String encodeValue(final byte[]value)
4171 {
4172 ensureNotNull(value);
4173
4174 final StringBuilder buffer = new StringBuilder();
4175 encodeValue(new ASN1OctetString(value), buffer);
4176 return buffer.toString();
4177 }
4178
4179
4180
4181 /**
4182 * Appends the assertion value for this filter to the provided buffer,
4183 * encoding any special characters as necessary.
4184 *
4185 * @param value The value to be encoded.
4186 * @param buffer The buffer to which the assertion value should be appended.
4187 */
4188 private static void encodeValue(final ASN1OctetString value,
4189 final StringBuilder buffer)
4190 {
4191 final byte[] valueBytes = value.getValue();
4192 for (int i=0; i < valueBytes.length; i++)
4193 {
4194 switch (numBytesInUTF8CharacterWithFirstByte(valueBytes[i]))
4195 {
4196 case 1:
4197 // This character is ASCII, but might still need to be escaped. We'll
4198 // escape anything
4199 if ((valueBytes[i] <= 0x1F) || // Non-printable ASCII characters.
4200 (valueBytes[i] == 0x28) || // Open parenthesis
4201 (valueBytes[i] == 0x29) || // Close parenthesis
4202 (valueBytes[i] == 0x2A) || // Asterisk
4203 (valueBytes[i] == 0x5C) || // Backslash
4204 (valueBytes[i] == 0x7F)) // DEL
4205 {
4206 buffer.append('\\');
4207 toHex(valueBytes[i], buffer);
4208 }
4209 else
4210 {
4211 buffer.append((char) valueBytes[i]);
4212 }
4213 break;
4214
4215 case 2:
4216 // If there are at least two bytes left, then we'll hex-encode the
4217 // next two bytes. Otherwise we'll hex-encode whatever is left.
4218 buffer.append('\\');
4219 toHex(valueBytes[i++], buffer);
4220 if (i < valueBytes.length)
4221 {
4222 buffer.append('\\');
4223 toHex(valueBytes[i], buffer);
4224 }
4225 break;
4226
4227 case 3:
4228 // If there are at least three bytes left, then we'll hex-encode the
4229 // next three bytes. Otherwise we'll hex-encode whatever is left.
4230 buffer.append('\\');
4231 toHex(valueBytes[i++], buffer);
4232 if (i < valueBytes.length)
4233 {
4234 buffer.append('\\');
4235 toHex(valueBytes[i++], buffer);
4236 }
4237 if (i < valueBytes.length)
4238 {
4239 buffer.append('\\');
4240 toHex(valueBytes[i], buffer);
4241 }
4242 break;
4243
4244 case 4:
4245 // If there are at least four bytes left, then we'll hex-encode the
4246 // next four bytes. Otherwise we'll hex-encode whatever is left.
4247 buffer.append('\\');
4248 toHex(valueBytes[i++], buffer);
4249 if (i < valueBytes.length)
4250 {
4251 buffer.append('\\');
4252 toHex(valueBytes[i++], buffer);
4253 }
4254 if (i < valueBytes.length)
4255 {
4256 buffer.append('\\');
4257 toHex(valueBytes[i++], buffer);
4258 }
4259 if (i < valueBytes.length)
4260 {
4261 buffer.append('\\');
4262 toHex(valueBytes[i], buffer);
4263 }
4264 break;
4265
4266 default:
4267 // We'll hex-encode whatever is left in the buffer.
4268 while (i < valueBytes.length)
4269 {
4270 buffer.append('\\');
4271 toHex(valueBytes[i++], buffer);
4272 }
4273 break;
4274 }
4275 }
4276 }
4277 }