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