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