001 /*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.ldap.sdk;
022
023
024
025 import java.io.Serializable;
026 import java.util.ArrayList;
027 import java.util.Arrays;
028 import java.util.HashSet;
029 import java.util.List;
030
031 import com.unboundid.asn1.ASN1Boolean;
032 import com.unboundid.asn1.ASN1Buffer;
033 import com.unboundid.asn1.ASN1BufferSequence;
034 import com.unboundid.asn1.ASN1BufferSet;
035 import com.unboundid.asn1.ASN1Element;
036 import com.unboundid.asn1.ASN1Exception;
037 import com.unboundid.asn1.ASN1OctetString;
038 import com.unboundid.asn1.ASN1Sequence;
039 import com.unboundid.asn1.ASN1Set;
040 import com.unboundid.asn1.ASN1StreamReader;
041 import com.unboundid.asn1.ASN1StreamReaderSequence;
042 import com.unboundid.asn1.ASN1StreamReaderSet;
043 import com.unboundid.ldap.matchingrules.CaseIgnoreStringMatchingRule;
044 import com.unboundid.ldap.matchingrules.MatchingRule;
045 import com.unboundid.ldap.sdk.schema.Schema;
046 import com.unboundid.util.ByteStringBuffer;
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 ByteStringBuffer valueBuffer = new ByteStringBuffer(r - l + 1);
1283 while (l <= r)
1284 {
1285 final char c = filterString.charAt(l);
1286 if (c == '\\')
1287 {
1288 l = readEscapedHexString(filterString, ++l, 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.toByteArray());
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 ByteStringBuffer buffer = new ByteStringBuffer(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(
1610 new ASN1OctetString(buffer.toByteArray()));
1611 buffer = new ByteStringBuffer(r - l + 1);
1612 }
1613 }
1614 else
1615 {
1616 // We haven't yet set the filter type, so the buffer must
1617 // contain the subInitial portion. We also know it's not
1618 // empty because of an earlier check.
1619 tempSubInitial =
1620 new ASN1OctetString(buffer.toByteArray());
1621 buffer = new ByteStringBuffer(r - l + 1);
1622 }
1623 }
1624
1625 tempFilterType = FILTER_TYPE_SUBSTRING;
1626 }
1627 break;
1628
1629 case '\\':
1630 l = readEscapedHexString(filterString, l, buffer);
1631 break;
1632
1633 case '(':
1634 throw new LDAPException(ResultCode.FILTER_ERROR,
1635 ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(
1636 l));
1637
1638 case ')':
1639 throw new LDAPException(ResultCode.FILTER_ERROR,
1640 ERR_FILTER_UNEXPECTED_CLOSE_PAREN.get(
1641 l));
1642
1643 default:
1644 buffer.append(c);
1645 break;
1646 }
1647 }
1648
1649 if ((tempFilterType == FILTER_TYPE_SUBSTRING) &&
1650 (buffer.length() > 0))
1651 {
1652 // The buffer must contain the subFinal portion.
1653 tempSubFinal = new ASN1OctetString(buffer.toByteArray());
1654 }
1655
1656 subInitial = tempSubInitial;
1657 subAny = subAnyList.toArray(new ASN1OctetString[subAnyList.size()]);
1658 subFinal = tempSubFinal;
1659
1660 if (tempFilterType == FILTER_TYPE_SUBSTRING)
1661 {
1662 assertionValue = null;
1663 }
1664 else
1665 {
1666 assertionValue = new ASN1OctetString(buffer.toByteArray());
1667 }
1668 }
1669
1670 filterType = tempFilterType;
1671 break;
1672 }
1673
1674
1675 if (startPos == 0)
1676 {
1677 return new Filter(filterString, filterType, filterComps, notComp,
1678 attrName, assertionValue, subInitial, subAny, subFinal,
1679 matchingRuleID, dnAttributes);
1680 }
1681 else
1682 {
1683 return new Filter(filterString.substring(startPos, endPos+1), filterType,
1684 filterComps, notComp, attrName, assertionValue,
1685 subInitial, subAny, subFinal, matchingRuleID,
1686 dnAttributes);
1687 }
1688 }
1689
1690
1691
1692 /**
1693 * Parses the specified portion of the provided filter string to obtain a set
1694 * of filter components for use in an AND or OR filter.
1695 *
1696 * @param filterString The string representation for the set of filters.
1697 * @param startPos The position of the first character to consider as
1698 * part of the first filter.
1699 * @param endPos The position of the last character to consider as
1700 * part of the last filter.
1701 * @param depth The current nesting depth for this filter. It should
1702 * be increased by one for each AND, OR, or NOT filter
1703 * encountered, in order to prevent stack overflow
1704 * errors from excessive recursion.
1705 *
1706 * @return The decoded set of search filters.
1707 *
1708 * @throws LDAPException If the provided string cannot be decoded as a set
1709 * of LDAP search filters.
1710 */
1711 private static Filter[] parseFilterComps(final String filterString,
1712 final int startPos, final int endPos,
1713 final int depth)
1714 throws LDAPException
1715 {
1716 if (startPos > endPos)
1717 {
1718 // This is acceptable, since it can represent an LDAP TRUE or FALSE filter
1719 // as described in RFC 4526.
1720 return NO_FILTERS;
1721 }
1722
1723
1724 // The set of filters must start with an opening parenthesis, and end with a
1725 // closing parenthesis.
1726 if (filterString.charAt(startPos) != '(')
1727 {
1728 throw new LDAPException(ResultCode.FILTER_ERROR,
1729 ERR_FILTER_EXPECTED_OPEN_PAREN.get(startPos));
1730 }
1731 if (filterString.charAt(endPos) != ')')
1732 {
1733 throw new LDAPException(ResultCode.FILTER_ERROR,
1734 ERR_FILTER_EXPECTED_CLOSE_PAREN.get(startPos));
1735 }
1736
1737
1738 // Iterate through the specified portion of the filter string and count
1739 // opening and closing parentheses to figure out where one filter ends and
1740 // another begins.
1741 final ArrayList<Filter> filterList = new ArrayList<Filter>(5);
1742 int filterStartPos = startPos;
1743 int pos = startPos;
1744 int numOpen = 0;
1745 while (pos <= endPos)
1746 {
1747 final char c = filterString.charAt(pos++);
1748 if (c == '(')
1749 {
1750 numOpen++;
1751 }
1752 else if (c == ')')
1753 {
1754 numOpen--;
1755 if (numOpen == 0)
1756 {
1757 filterList.add(create(filterString, filterStartPos, pos-1, depth));
1758 filterStartPos = pos;
1759 }
1760 }
1761 }
1762
1763 if (numOpen != 0)
1764 {
1765 throw new LDAPException(ResultCode.FILTER_ERROR,
1766 ERR_FILTER_MISMATCHED_PARENS.get(startPos,
1767 endPos));
1768 }
1769
1770 return filterList.toArray(new Filter[filterList.size()]);
1771 }
1772
1773
1774
1775 /**
1776 * Reads one or more hex-encoded bytes from the specified portion of the
1777 * filter string.
1778 *
1779 * @param filterString The string from which the data is to be read.
1780 * @param startPos The position at which to start reading. This should
1781 * be the position of first hex character immediately
1782 * after the initial backslash.
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,
1793 final ByteStringBuffer buffer)
1794 throws LDAPException
1795 {
1796 byte b;
1797 switch (filterString.charAt(startPos))
1798 {
1799 case '0':
1800 b = 0x00;
1801 break;
1802 case '1':
1803 b = 0x10;
1804 break;
1805 case '2':
1806 b = 0x20;
1807 break;
1808 case '3':
1809 b = 0x30;
1810 break;
1811 case '4':
1812 b = 0x40;
1813 break;
1814 case '5':
1815 b = 0x50;
1816 break;
1817 case '6':
1818 b = 0x60;
1819 break;
1820 case '7':
1821 b = 0x70;
1822 break;
1823 case '8':
1824 b = (byte) 0x80;
1825 break;
1826 case '9':
1827 b = (byte) 0x90;
1828 break;
1829 case 'a':
1830 case 'A':
1831 b = (byte) 0xA0;
1832 break;
1833 case 'b':
1834 case 'B':
1835 b = (byte) 0xB0;
1836 break;
1837 case 'c':
1838 case 'C':
1839 b = (byte) 0xC0;
1840 break;
1841 case 'd':
1842 case 'D':
1843 b = (byte) 0xD0;
1844 break;
1845 case 'e':
1846 case 'E':
1847 b = (byte) 0xE0;
1848 break;
1849 case 'f':
1850 case 'F':
1851 b = (byte) 0xF0;
1852 break;
1853 default:
1854 throw new LDAPException(ResultCode.FILTER_ERROR,
1855 ERR_FILTER_INVALID_HEX_CHAR.get(filterString.charAt(startPos),
1856 startPos));
1857 }
1858
1859 switch (filterString.charAt(startPos+1))
1860 {
1861 case '0':
1862 // No action is required.
1863 break;
1864 case '1':
1865 b |= 0x01;
1866 break;
1867 case '2':
1868 b |= 0x02;
1869 break;
1870 case '3':
1871 b |= 0x03;
1872 break;
1873 case '4':
1874 b |= 0x04;
1875 break;
1876 case '5':
1877 b |= 0x05;
1878 break;
1879 case '6':
1880 b |= 0x06;
1881 break;
1882 case '7':
1883 b |= 0x07;
1884 break;
1885 case '8':
1886 b |= 0x08;
1887 break;
1888 case '9':
1889 b |= 0x09;
1890 break;
1891 case 'a':
1892 case 'A':
1893 b |= 0x0A;
1894 break;
1895 case 'b':
1896 case 'B':
1897 b |= 0x0B;
1898 break;
1899 case 'c':
1900 case 'C':
1901 b |= 0x0C;
1902 break;
1903 case 'd':
1904 case 'D':
1905 b |= 0x0D;
1906 break;
1907 case 'e':
1908 case 'E':
1909 b |= 0x0E;
1910 break;
1911 case 'f':
1912 case 'F':
1913 b |= 0x0F;
1914 break;
1915 default:
1916 throw new LDAPException(ResultCode.FILTER_ERROR,
1917 ERR_FILTER_INVALID_HEX_CHAR.get(filterString.charAt(startPos+1),
1918 (startPos+1)));
1919 }
1920
1921 buffer.append(b);
1922 return startPos+2;
1923 }
1924
1925
1926
1927 /**
1928 * Writes an ASN.1-encoded representation of this filter to the provided ASN.1
1929 * buffer.
1930 *
1931 * @param buffer The ASN.1 buffer to which the encoded representation should
1932 * be written.
1933 */
1934 public void writeTo(final ASN1Buffer buffer)
1935 {
1936 switch (filterType)
1937 {
1938 case FILTER_TYPE_AND:
1939 case FILTER_TYPE_OR:
1940 final ASN1BufferSet compSet = buffer.beginSet(filterType);
1941 for (final Filter f : filterComps)
1942 {
1943 f.writeTo(buffer);
1944 }
1945 compSet.end();
1946 break;
1947
1948 case FILTER_TYPE_NOT:
1949 buffer.addElement(
1950 new ASN1Element(filterType, notComp.encode().encode()));
1951 break;
1952
1953 case FILTER_TYPE_EQUALITY:
1954 case FILTER_TYPE_GREATER_OR_EQUAL:
1955 case FILTER_TYPE_LESS_OR_EQUAL:
1956 case FILTER_TYPE_APPROXIMATE_MATCH:
1957 final ASN1BufferSequence avaSequence = buffer.beginSequence(filterType);
1958 buffer.addOctetString(attrName);
1959 buffer.addElement(assertionValue);
1960 avaSequence.end();
1961 break;
1962
1963 case FILTER_TYPE_SUBSTRING:
1964 final ASN1BufferSequence subFilterSequence =
1965 buffer.beginSequence(filterType);
1966 buffer.addOctetString(attrName);
1967
1968 final ASN1BufferSequence valueSequence = buffer.beginSequence();
1969 if (subInitial != null)
1970 {
1971 buffer.addOctetString(SUBSTRING_TYPE_SUBINITIAL,
1972 subInitial.getValue());
1973 }
1974
1975 for (final ASN1OctetString s : subAny)
1976 {
1977 buffer.addOctetString(SUBSTRING_TYPE_SUBANY, s.getValue());
1978 }
1979
1980 if (subFinal != null)
1981 {
1982 buffer.addOctetString(SUBSTRING_TYPE_SUBFINAL, subFinal.getValue());
1983 }
1984 valueSequence.end();
1985 subFilterSequence.end();
1986 break;
1987
1988 case FILTER_TYPE_PRESENCE:
1989 buffer.addOctetString(filterType, attrName);
1990 break;
1991
1992 case FILTER_TYPE_EXTENSIBLE_MATCH:
1993 final ASN1BufferSequence mrSequence = buffer.beginSequence(filterType);
1994 if (matchingRuleID != null)
1995 {
1996 buffer.addOctetString(EXTENSIBLE_TYPE_MATCHING_RULE_ID,
1997 matchingRuleID);
1998 }
1999
2000 if (attrName != null)
2001 {
2002 buffer.addOctetString(EXTENSIBLE_TYPE_ATTRIBUTE_NAME, attrName);
2003 }
2004
2005 buffer.addOctetString(EXTENSIBLE_TYPE_MATCH_VALUE,
2006 assertionValue.getValue());
2007
2008 if (dnAttributes)
2009 {
2010 buffer.addBoolean(EXTENSIBLE_TYPE_DN_ATTRIBUTES, true);
2011 }
2012 mrSequence.end();
2013 break;
2014 }
2015 }
2016
2017
2018
2019 /**
2020 * Encodes this search filter to an ASN.1 element suitable for inclusion in an
2021 * LDAP search request protocol op.
2022 *
2023 * @return An ASN.1 element containing the encoded search filter.
2024 */
2025 public ASN1Element encode()
2026 {
2027 switch (filterType)
2028 {
2029 case FILTER_TYPE_AND:
2030 case FILTER_TYPE_OR:
2031 final ASN1Element[] filterElements =
2032 new ASN1Element[filterComps.length];
2033 for (int i=0; i < filterComps.length; i++)
2034 {
2035 filterElements[i] = filterComps[i].encode();
2036 }
2037 return new ASN1Set(filterType, filterElements);
2038
2039
2040 case FILTER_TYPE_NOT:
2041 return new ASN1Element(filterType, notComp.encode().encode());
2042
2043
2044 case FILTER_TYPE_EQUALITY:
2045 case FILTER_TYPE_GREATER_OR_EQUAL:
2046 case FILTER_TYPE_LESS_OR_EQUAL:
2047 case FILTER_TYPE_APPROXIMATE_MATCH:
2048 final ASN1OctetString[] attrValueAssertionElements =
2049 {
2050 new ASN1OctetString(attrName),
2051 assertionValue
2052 };
2053 return new ASN1Sequence(filterType, attrValueAssertionElements);
2054
2055
2056 case FILTER_TYPE_SUBSTRING:
2057 final ArrayList<ASN1OctetString> subList =
2058 new ArrayList<ASN1OctetString>(2 + subAny.length);
2059 if (subInitial != null)
2060 {
2061 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBINITIAL,
2062 subInitial.getValue()));
2063 }
2064
2065 for (final ASN1Element subAnyElement : subAny)
2066 {
2067 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBANY,
2068 subAnyElement.getValue()));
2069 }
2070
2071
2072 if (subFinal != null)
2073 {
2074 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBFINAL,
2075 subFinal.getValue()));
2076 }
2077
2078 final ASN1Element[] subFilterElements =
2079 {
2080 new ASN1OctetString(attrName),
2081 new ASN1Sequence(subList)
2082 };
2083 return new ASN1Sequence(filterType, subFilterElements);
2084
2085
2086 case FILTER_TYPE_PRESENCE:
2087 return new ASN1OctetString(filterType, attrName);
2088
2089
2090 case FILTER_TYPE_EXTENSIBLE_MATCH:
2091 final ArrayList<ASN1Element> emElementList =
2092 new ArrayList<ASN1Element>(4);
2093 if (matchingRuleID != null)
2094 {
2095 emElementList.add(new ASN1OctetString(
2096 EXTENSIBLE_TYPE_MATCHING_RULE_ID, matchingRuleID));
2097 }
2098
2099 if (attrName != null)
2100 {
2101 emElementList.add(new ASN1OctetString(
2102 EXTENSIBLE_TYPE_ATTRIBUTE_NAME, attrName));
2103 }
2104
2105 emElementList.add(new ASN1OctetString(EXTENSIBLE_TYPE_MATCH_VALUE,
2106 assertionValue.getValue()));
2107
2108 if (dnAttributes)
2109 {
2110 emElementList.add(new ASN1Boolean(EXTENSIBLE_TYPE_DN_ATTRIBUTES,
2111 true));
2112 }
2113
2114 return new ASN1Sequence(filterType, emElementList);
2115
2116
2117 default:
2118 throw new AssertionError(ERR_FILTER_INVALID_TYPE.get(
2119 toHex(filterType)));
2120 }
2121 }
2122
2123
2124
2125 /**
2126 * Reads and decodes a search filter from the provided ASN.1 stream reader.
2127 *
2128 * @param reader The ASN.1 stream reader from which to read the filter.
2129 *
2130 * @return The decoded search filter.
2131 *
2132 * @throws LDAPException If an error occurs while reading or parsing the
2133 * search filter.
2134 */
2135 public static Filter readFrom(final ASN1StreamReader reader)
2136 throws LDAPException
2137 {
2138 try
2139 {
2140 final Filter[] filterComps;
2141 final Filter notComp;
2142 final String attrName;
2143 final ASN1OctetString assertionValue;
2144 final ASN1OctetString subInitial;
2145 final ASN1OctetString[] subAny;
2146 final ASN1OctetString subFinal;
2147 final String matchingRuleID;
2148 final boolean dnAttributes;
2149
2150 final byte filterType = (byte) reader.peek();
2151
2152 switch (filterType)
2153 {
2154 case FILTER_TYPE_AND:
2155 case FILTER_TYPE_OR:
2156 final ArrayList<Filter> comps = new ArrayList<Filter>(5);
2157 final ASN1StreamReaderSet elementSet = reader.beginSet();
2158 while (elementSet.hasMoreElements())
2159 {
2160 comps.add(readFrom(reader));
2161 }
2162
2163 filterComps = new Filter[comps.size()];
2164 comps.toArray(filterComps);
2165
2166 notComp = null;
2167 attrName = null;
2168 assertionValue = null;
2169 subInitial = null;
2170 subAny = NO_SUB_ANY;
2171 subFinal = null;
2172 matchingRuleID = null;
2173 dnAttributes = false;
2174 break;
2175
2176
2177 case FILTER_TYPE_NOT:
2178 final ASN1Element notFilterElement;
2179 try
2180 {
2181 final ASN1Element e = reader.readElement();
2182 notFilterElement = ASN1Element.decode(e.getValue());
2183 }
2184 catch (final ASN1Exception ae)
2185 {
2186 debugException(ae);
2187 throw new LDAPException(ResultCode.DECODING_ERROR,
2188 ERR_FILTER_CANNOT_DECODE_NOT_COMP.get(getExceptionMessage(ae)),
2189 ae);
2190 }
2191 notComp = decode(notFilterElement);
2192
2193 filterComps = NO_FILTERS;
2194 attrName = null;
2195 assertionValue = null;
2196 subInitial = null;
2197 subAny = NO_SUB_ANY;
2198 subFinal = null;
2199 matchingRuleID = null;
2200 dnAttributes = false;
2201 break;
2202
2203
2204 case FILTER_TYPE_EQUALITY:
2205 case FILTER_TYPE_GREATER_OR_EQUAL:
2206 case FILTER_TYPE_LESS_OR_EQUAL:
2207 case FILTER_TYPE_APPROXIMATE_MATCH:
2208 reader.beginSequence();
2209 attrName = reader.readString();
2210 assertionValue = new ASN1OctetString(reader.readBytes());
2211
2212 filterComps = NO_FILTERS;
2213 notComp = null;
2214 subInitial = null;
2215 subAny = NO_SUB_ANY;
2216 subFinal = null;
2217 matchingRuleID = null;
2218 dnAttributes = false;
2219 break;
2220
2221
2222 case FILTER_TYPE_SUBSTRING:
2223 reader.beginSequence();
2224 attrName = reader.readString();
2225
2226 ASN1OctetString tempSubInitial = null;
2227 ASN1OctetString tempSubFinal = null;
2228 final ArrayList<ASN1OctetString> subAnyList =
2229 new ArrayList<ASN1OctetString>(1);
2230 final ASN1StreamReaderSequence subSequence = reader.beginSequence();
2231 while (subSequence.hasMoreElements())
2232 {
2233 final byte type = (byte) reader.peek();
2234 final ASN1OctetString s =
2235 new ASN1OctetString(type, reader.readBytes());
2236 switch (type)
2237 {
2238 case SUBSTRING_TYPE_SUBINITIAL:
2239 tempSubInitial = s;
2240 break;
2241 case SUBSTRING_TYPE_SUBANY:
2242 subAnyList.add(s);
2243 break;
2244 case SUBSTRING_TYPE_SUBFINAL:
2245 tempSubFinal = s;
2246 break;
2247 default:
2248 throw new LDAPException(ResultCode.DECODING_ERROR,
2249 ERR_FILTER_INVALID_SUBSTR_TYPE.get(toHex(type)));
2250 }
2251 }
2252
2253 subInitial = tempSubInitial;
2254 subFinal = tempSubFinal;
2255
2256 subAny = new ASN1OctetString[subAnyList.size()];
2257 subAnyList.toArray(subAny);
2258
2259 filterComps = NO_FILTERS;
2260 notComp = null;
2261 assertionValue = null;
2262 matchingRuleID = null;
2263 dnAttributes = false;
2264 break;
2265
2266
2267 case FILTER_TYPE_PRESENCE:
2268 attrName = reader.readString();
2269
2270 filterComps = NO_FILTERS;
2271 notComp = null;
2272 assertionValue = null;
2273 subInitial = null;
2274 subAny = NO_SUB_ANY;
2275 subFinal = null;
2276 matchingRuleID = null;
2277 dnAttributes = false;
2278 break;
2279
2280
2281 case FILTER_TYPE_EXTENSIBLE_MATCH:
2282 String tempAttrName = null;
2283 ASN1OctetString tempAssertionValue = null;
2284 String tempMatchingRuleID = null;
2285 boolean tempDNAttributes = false;
2286
2287 final ASN1StreamReaderSequence emSequence = reader.beginSequence();
2288 while (emSequence.hasMoreElements())
2289 {
2290 final byte type = (byte) reader.peek();
2291 switch (type)
2292 {
2293 case EXTENSIBLE_TYPE_ATTRIBUTE_NAME:
2294 tempAttrName = reader.readString();
2295 break;
2296 case EXTENSIBLE_TYPE_MATCHING_RULE_ID:
2297 tempMatchingRuleID = reader.readString();
2298 break;
2299 case EXTENSIBLE_TYPE_MATCH_VALUE:
2300 tempAssertionValue =
2301 new ASN1OctetString(type, reader.readBytes());
2302 break;
2303 case EXTENSIBLE_TYPE_DN_ATTRIBUTES:
2304 tempDNAttributes = reader.readBoolean();
2305 break;
2306 default:
2307 throw new LDAPException(ResultCode.DECODING_ERROR,
2308 ERR_FILTER_EXTMATCH_INVALID_TYPE.get(toHex(type)));
2309 }
2310 }
2311
2312 if ((tempAttrName == null) && (tempMatchingRuleID == null))
2313 {
2314 throw new LDAPException(ResultCode.DECODING_ERROR,
2315 ERR_FILTER_EXTMATCH_NO_ATTR_OR_MRID.get());
2316 }
2317
2318 if (tempAssertionValue == null)
2319 {
2320 throw new LDAPException(ResultCode.DECODING_ERROR,
2321 ERR_FILTER_EXTMATCH_NO_VALUE.get());
2322 }
2323
2324 attrName = tempAttrName;
2325 assertionValue = tempAssertionValue;
2326 matchingRuleID = tempMatchingRuleID;
2327 dnAttributes = tempDNAttributes;
2328
2329 filterComps = NO_FILTERS;
2330 notComp = null;
2331 subInitial = null;
2332 subAny = NO_SUB_ANY;
2333 subFinal = null;
2334 break;
2335
2336
2337 default:
2338 throw new LDAPException(ResultCode.DECODING_ERROR,
2339 ERR_FILTER_ELEMENT_INVALID_TYPE.get(toHex(filterType)));
2340 }
2341
2342 return new Filter(null, filterType, filterComps, notComp, attrName,
2343 assertionValue, subInitial, subAny, subFinal,
2344 matchingRuleID, dnAttributes);
2345 }
2346 catch (LDAPException le)
2347 {
2348 debugException(le);
2349 throw le;
2350 }
2351 catch (Exception e)
2352 {
2353 debugException(e);
2354 throw new LDAPException(ResultCode.DECODING_ERROR,
2355 ERR_FILTER_CANNOT_DECODE.get(getExceptionMessage(e)), e);
2356 }
2357 }
2358
2359
2360
2361 /**
2362 * Decodes the provided ASN.1 element as a search filter.
2363 *
2364 * @param filterElement The ASN.1 element containing the encoded search
2365 * filter.
2366 *
2367 * @return The decoded search filter.
2368 *
2369 * @throws LDAPException If the provided ASN.1 element cannot be decoded as
2370 * a search filter.
2371 */
2372 public static Filter decode(final ASN1Element filterElement)
2373 throws LDAPException
2374 {
2375 final byte filterType = filterElement.getType();
2376 final Filter[] filterComps;
2377 final Filter notComp;
2378 final String attrName;
2379 final ASN1OctetString assertionValue;
2380 final ASN1OctetString subInitial;
2381 final ASN1OctetString[] subAny;
2382 final ASN1OctetString subFinal;
2383 final String matchingRuleID;
2384 final boolean dnAttributes;
2385
2386 switch (filterType)
2387 {
2388 case FILTER_TYPE_AND:
2389 case FILTER_TYPE_OR:
2390 notComp = null;
2391 attrName = null;
2392 assertionValue = null;
2393 subInitial = null;
2394 subAny = NO_SUB_ANY;
2395 subFinal = null;
2396 matchingRuleID = null;
2397 dnAttributes = false;
2398
2399 final ASN1Set compSet;
2400 try
2401 {
2402 compSet = ASN1Set.decodeAsSet(filterElement);
2403 }
2404 catch (final ASN1Exception ae)
2405 {
2406 debugException(ae);
2407 throw new LDAPException(ResultCode.DECODING_ERROR,
2408 ERR_FILTER_CANNOT_DECODE_COMPS.get(getExceptionMessage(ae)), ae);
2409 }
2410
2411 final ASN1Element[] compElements = compSet.elements();
2412 filterComps = new Filter[compElements.length];
2413 for (int i=0; i < compElements.length; i++)
2414 {
2415 filterComps[i] = decode(compElements[i]);
2416 }
2417 break;
2418
2419
2420 case FILTER_TYPE_NOT:
2421 filterComps = NO_FILTERS;
2422 attrName = null;
2423 assertionValue = null;
2424 subInitial = null;
2425 subAny = NO_SUB_ANY;
2426 subFinal = null;
2427 matchingRuleID = null;
2428 dnAttributes = false;
2429
2430 final ASN1Element notFilterElement;
2431 try
2432 {
2433 notFilterElement = ASN1Element.decode(filterElement.getValue());
2434 }
2435 catch (final ASN1Exception ae)
2436 {
2437 debugException(ae);
2438 throw new LDAPException(ResultCode.DECODING_ERROR,
2439 ERR_FILTER_CANNOT_DECODE_NOT_COMP.get(getExceptionMessage(ae)),
2440 ae);
2441 }
2442 notComp = decode(notFilterElement);
2443 break;
2444
2445
2446
2447 case FILTER_TYPE_EQUALITY:
2448 case FILTER_TYPE_GREATER_OR_EQUAL:
2449 case FILTER_TYPE_LESS_OR_EQUAL:
2450 case FILTER_TYPE_APPROXIMATE_MATCH:
2451 filterComps = NO_FILTERS;
2452 notComp = null;
2453 subInitial = null;
2454 subAny = NO_SUB_ANY;
2455 subFinal = null;
2456 matchingRuleID = null;
2457 dnAttributes = false;
2458
2459 final ASN1Sequence avaSequence;
2460 try
2461 {
2462 avaSequence = ASN1Sequence.decodeAsSequence(filterElement);
2463 }
2464 catch (final ASN1Exception ae)
2465 {
2466 debugException(ae);
2467 throw new LDAPException(ResultCode.DECODING_ERROR,
2468 ERR_FILTER_CANNOT_DECODE_AVA.get(getExceptionMessage(ae)), ae);
2469 }
2470
2471 final ASN1Element[] avaElements = avaSequence.elements();
2472 if (avaElements.length != 2)
2473 {
2474 throw new LDAPException(ResultCode.DECODING_ERROR,
2475 ERR_FILTER_INVALID_AVA_ELEMENT_COUNT.get(
2476 avaElements.length));
2477 }
2478
2479 attrName =
2480 ASN1OctetString.decodeAsOctetString(avaElements[0]).stringValue();
2481 assertionValue = ASN1OctetString.decodeAsOctetString(avaElements[1]);
2482 break;
2483
2484
2485 case FILTER_TYPE_SUBSTRING:
2486 filterComps = NO_FILTERS;
2487 notComp = null;
2488 assertionValue = null;
2489 matchingRuleID = null;
2490 dnAttributes = false;
2491
2492 final ASN1Sequence subFilterSequence;
2493 try
2494 {
2495 subFilterSequence = ASN1Sequence.decodeAsSequence(filterElement);
2496 }
2497 catch (final ASN1Exception ae)
2498 {
2499 debugException(ae);
2500 throw new LDAPException(ResultCode.DECODING_ERROR,
2501 ERR_FILTER_CANNOT_DECODE_SUBSTRING.get(getExceptionMessage(ae)),
2502 ae);
2503 }
2504
2505 final ASN1Element[] subFilterElements = subFilterSequence.elements();
2506 if (subFilterElements.length != 2)
2507 {
2508 throw new LDAPException(ResultCode.DECODING_ERROR,
2509 ERR_FILTER_INVALID_SUBSTR_ASSERTION_COUNT.get(
2510 subFilterElements.length));
2511 }
2512
2513 attrName = ASN1OctetString.decodeAsOctetString(
2514 subFilterElements[0]).stringValue();
2515
2516 final ASN1Sequence subSequence;
2517 try
2518 {
2519 subSequence = ASN1Sequence.decodeAsSequence(subFilterElements[1]);
2520 }
2521 catch (ASN1Exception ae)
2522 {
2523 debugException(ae);
2524 throw new LDAPException(ResultCode.DECODING_ERROR,
2525 ERR_FILTER_CANNOT_DECODE_SUBSTRING.get(getExceptionMessage(ae)),
2526 ae);
2527 }
2528
2529 ASN1OctetString tempSubInitial = null;
2530 ASN1OctetString tempSubFinal = null;
2531 final ArrayList<ASN1OctetString> subAnyList =
2532 new ArrayList<ASN1OctetString>(1);
2533
2534 final ASN1Element[] subElements = subSequence.elements();
2535 for (final ASN1Element subElement : subElements)
2536 {
2537 switch (subElement.getType())
2538 {
2539 case SUBSTRING_TYPE_SUBINITIAL:
2540 if (tempSubInitial == null)
2541 {
2542 tempSubInitial =
2543 ASN1OctetString.decodeAsOctetString(subElement);
2544 }
2545 else
2546 {
2547 throw new LDAPException(ResultCode.DECODING_ERROR,
2548 ERR_FILTER_MULTIPLE_SUBINITIAL.get());
2549 }
2550 break;
2551
2552 case SUBSTRING_TYPE_SUBANY:
2553 subAnyList.add(ASN1OctetString.decodeAsOctetString(subElement));
2554 break;
2555
2556 case SUBSTRING_TYPE_SUBFINAL:
2557 if (tempSubFinal == null)
2558 {
2559 tempSubFinal = ASN1OctetString.decodeAsOctetString(subElement);
2560 }
2561 else
2562 {
2563 throw new LDAPException(ResultCode.DECODING_ERROR,
2564 ERR_FILTER_MULTIPLE_SUBFINAL.get());
2565 }
2566 break;
2567
2568 default:
2569 throw new LDAPException(ResultCode.DECODING_ERROR,
2570 ERR_FILTER_INVALID_SUBSTR_TYPE.get(
2571 toHex(subElement.getType())));
2572 }
2573 }
2574
2575 subInitial = tempSubInitial;
2576 subAny = subAnyList.toArray(new ASN1OctetString[subAnyList.size()]);
2577 subFinal = tempSubFinal;
2578 break;
2579
2580
2581 case FILTER_TYPE_PRESENCE:
2582 filterComps = NO_FILTERS;
2583 notComp = null;
2584 assertionValue = null;
2585 subInitial = null;
2586 subAny = NO_SUB_ANY;
2587 subFinal = null;
2588 matchingRuleID = null;
2589 dnAttributes = false;
2590 attrName =
2591 ASN1OctetString.decodeAsOctetString(filterElement).stringValue();
2592 break;
2593
2594
2595 case FILTER_TYPE_EXTENSIBLE_MATCH:
2596 filterComps = NO_FILTERS;
2597 notComp = null;
2598 subInitial = null;
2599 subAny = NO_SUB_ANY;
2600 subFinal = null;
2601
2602 final ASN1Sequence emSequence;
2603 try
2604 {
2605 emSequence = ASN1Sequence.decodeAsSequence(filterElement);
2606 }
2607 catch (ASN1Exception ae)
2608 {
2609 debugException(ae);
2610 throw new LDAPException(ResultCode.DECODING_ERROR,
2611 ERR_FILTER_CANNOT_DECODE_EXTMATCH.get(getExceptionMessage(ae)),
2612 ae);
2613 }
2614
2615 String tempAttrName = null;
2616 ASN1OctetString tempAssertionValue = null;
2617 String tempMatchingRuleID = null;
2618 boolean tempDNAttributes = false;
2619 for (final ASN1Element e : emSequence.elements())
2620 {
2621 switch (e.getType())
2622 {
2623 case EXTENSIBLE_TYPE_ATTRIBUTE_NAME:
2624 if (tempAttrName == null)
2625 {
2626 tempAttrName =
2627 ASN1OctetString.decodeAsOctetString(e).stringValue();
2628 }
2629 else
2630 {
2631 throw new LDAPException(ResultCode.DECODING_ERROR,
2632 ERR_FILTER_EXTMATCH_MULTIPLE_ATTRS.get());
2633 }
2634 break;
2635
2636 case EXTENSIBLE_TYPE_MATCHING_RULE_ID:
2637 if (tempMatchingRuleID == null)
2638 {
2639 tempMatchingRuleID =
2640 ASN1OctetString.decodeAsOctetString(e).stringValue();
2641 }
2642 else
2643 {
2644 throw new LDAPException(ResultCode.DECODING_ERROR,
2645 ERR_FILTER_EXTMATCH_MULTIPLE_MRIDS.get());
2646 }
2647 break;
2648
2649 case EXTENSIBLE_TYPE_MATCH_VALUE:
2650 if (tempAssertionValue == null)
2651 {
2652 tempAssertionValue = ASN1OctetString.decodeAsOctetString(e);
2653 }
2654 else
2655 {
2656 throw new LDAPException(ResultCode.DECODING_ERROR,
2657 ERR_FILTER_EXTMATCH_MULTIPLE_VALUES.get());
2658 }
2659 break;
2660
2661 case EXTENSIBLE_TYPE_DN_ATTRIBUTES:
2662 try
2663 {
2664 if (tempDNAttributes)
2665 {
2666 throw new LDAPException(ResultCode.DECODING_ERROR,
2667 ERR_FILTER_EXTMATCH_MULTIPLE_DNATTRS.get());
2668 }
2669 else
2670 {
2671 tempDNAttributes =
2672 ASN1Boolean.decodeAsBoolean(e).booleanValue();
2673 }
2674 }
2675 catch (ASN1Exception ae)
2676 {
2677 debugException(ae);
2678 throw new LDAPException(ResultCode.DECODING_ERROR,
2679 ERR_FILTER_EXTMATCH_DNATTRS_NOT_BOOLEAN.get(
2680 getExceptionMessage(ae)),
2681 ae);
2682 }
2683 break;
2684
2685 default:
2686 throw new LDAPException(ResultCode.DECODING_ERROR,
2687 ERR_FILTER_EXTMATCH_INVALID_TYPE.get(
2688 toHex(e.getType())));
2689 }
2690 }
2691
2692 if ((tempAttrName == null) && (tempMatchingRuleID == null))
2693 {
2694 throw new LDAPException(ResultCode.DECODING_ERROR,
2695 ERR_FILTER_EXTMATCH_NO_ATTR_OR_MRID.get());
2696 }
2697
2698 if (tempAssertionValue == null)
2699 {
2700 throw new LDAPException(ResultCode.DECODING_ERROR,
2701 ERR_FILTER_EXTMATCH_NO_VALUE.get());
2702 }
2703
2704 attrName = tempAttrName;
2705 assertionValue = tempAssertionValue;
2706 matchingRuleID = tempMatchingRuleID;
2707 dnAttributes = tempDNAttributes;
2708 break;
2709
2710
2711 default:
2712 throw new LDAPException(ResultCode.DECODING_ERROR,
2713 ERR_FILTER_ELEMENT_INVALID_TYPE.get(
2714 toHex(filterElement.getType())));
2715 }
2716
2717
2718 return new Filter(null, filterType, filterComps, notComp, attrName,
2719 assertionValue, subInitial, subAny, subFinal,
2720 matchingRuleID, dnAttributes);
2721 }
2722
2723
2724
2725 /**
2726 * Retrieves the filter type for this filter.
2727 *
2728 * @return The filter type for this filter.
2729 */
2730 public byte getFilterType()
2731 {
2732 return filterType;
2733 }
2734
2735
2736
2737 /**
2738 * Retrieves the set of filter components used in this AND or OR filter. This
2739 * is not applicable for any other filter type.
2740 *
2741 * @return The set of filter components used in this AND or OR filter, or an
2742 * empty array if this is some other type of filter or if there are
2743 * no components (i.e., as in an LDAP TRUE or LDAP FALSE filter).
2744 */
2745 public Filter[] getComponents()
2746 {
2747 return filterComps;
2748 }
2749
2750
2751
2752 /**
2753 * Retrieves the filter component used in this NOT filter. This is not
2754 * applicable for any other filter type.
2755 *
2756 * @return The filter component used in this NOT filter, or {@code null} if
2757 * this is some other type of filter.
2758 */
2759 public Filter getNOTComponent()
2760 {
2761 return notComp;
2762 }
2763
2764
2765
2766 /**
2767 * Retrieves the name of the attribute type for this search filter. This is
2768 * applicable for the following types of filters:
2769 * <UL>
2770 * <LI>Equality</LI>
2771 * <LI>Substring</LI>
2772 * <LI>Greater or Equal</LI>
2773 * <LI>Less or Equal</LI>
2774 * <LI>Presence</LI>
2775 * <LI>Approximate Match</LI>
2776 * <LI>Extensible Match</LI>
2777 * </UL>
2778 *
2779 * @return The name of the attribute type for this search filter, or
2780 * {@code null} if it is not applicable for this type of filter.
2781 */
2782 public String getAttributeName()
2783 {
2784 return attrName;
2785 }
2786
2787
2788
2789 /**
2790 * Retrieves the string representation of the assertion value for this search
2791 * filter. This is applicable for the following types of filters:
2792 * <UL>
2793 * <LI>Equality</LI>
2794 * <LI>Greater or Equal</LI>
2795 * <LI>Less or Equal</LI>
2796 * <LI>Approximate Match</LI>
2797 * <LI>Extensible Match</LI>
2798 * </UL>
2799 *
2800 * @return The string representation of the assertion value for this search
2801 * filter, or {@code null} if it is not applicable for this type of
2802 * filter.
2803 */
2804 public String getAssertionValue()
2805 {
2806 if (assertionValue == null)
2807 {
2808 return null;
2809 }
2810 else
2811 {
2812 return assertionValue.stringValue();
2813 }
2814 }
2815
2816
2817
2818 /**
2819 * Retrieves the binary representation of the assertion value for this search
2820 * filter. This is applicable for the following types of filters:
2821 * <UL>
2822 * <LI>Equality</LI>
2823 * <LI>Greater or Equal</LI>
2824 * <LI>Less or Equal</LI>
2825 * <LI>Approximate Match</LI>
2826 * <LI>Extensible Match</LI>
2827 * </UL>
2828 *
2829 * @return The binary representation of the assertion value for this search
2830 * filter, or {@code null} if it is not applicable for this type of
2831 * filter.
2832 */
2833 public byte[] getAssertionValueBytes()
2834 {
2835 if (assertionValue == null)
2836 {
2837 return null;
2838 }
2839 else
2840 {
2841 return assertionValue.getValue();
2842 }
2843 }
2844
2845
2846
2847 /**
2848 * Retrieves the raw assertion value for this search filter as an ASN.1
2849 * octet string. This is applicable for the following types of filters:
2850 * <UL>
2851 * <LI>Equality</LI>
2852 * <LI>Greater or Equal</LI>
2853 * <LI>Less or Equal</LI>
2854 * <LI>Approximate Match</LI>
2855 * <LI>Extensible Match</LI>
2856 * </UL>
2857 *
2858 * @return The raw assertion value for this search filter as an ASN.1 octet
2859 * string, or {@code null} if it is not applicable for this type of
2860 * filter.
2861 */
2862 public ASN1OctetString getRawAssertionValue()
2863 {
2864 return assertionValue;
2865 }
2866
2867
2868
2869 /**
2870 * Retrieves the string representation of the subInitial element for this
2871 * substring filter. This is not applicable for any other filter type.
2872 *
2873 * @return The string representation of the subInitial element for this
2874 * substring filter, or {@code null} if this is some other type of
2875 * filter, or if it is a substring filter with no subInitial element.
2876 */
2877 public String getSubInitialString()
2878 {
2879 if (subInitial == null)
2880 {
2881 return null;
2882 }
2883 else
2884 {
2885 return subInitial.stringValue();
2886 }
2887 }
2888
2889
2890
2891 /**
2892 * Retrieves the binary representation of the subInitial element for this
2893 * substring filter. This is not applicable for any other filter type.
2894 *
2895 * @return The binary representation of the subInitial element for this
2896 * substring filter, or {@code null} if this is some other type of
2897 * filter, or if it is a substring filter with no subInitial element.
2898 */
2899 public byte[] getSubInitialBytes()
2900 {
2901 if (subInitial == null)
2902 {
2903 return null;
2904 }
2905 else
2906 {
2907 return subInitial.getValue();
2908 }
2909 }
2910
2911
2912
2913 /**
2914 * Retrieves the raw subInitial element for this filter as an ASN.1 octet
2915 * string. This is not applicable for any other filter type.
2916 *
2917 * @return The raw subInitial element for this filter as an ASN.1 octet
2918 * string, or {@code null} if this is not a substring filter, or if
2919 * it is a substring filter with no subInitial element.
2920 */
2921 public ASN1OctetString getRawSubInitialValue()
2922 {
2923 return subInitial;
2924 }
2925
2926
2927
2928 /**
2929 * Retrieves the string representations of the subAny elements for this
2930 * substring filter. This is not applicable for any other filter type.
2931 *
2932 * @return The string representations of the subAny elements for this
2933 * substring filter, or an empty array if this is some other type of
2934 * filter, or if it is a substring filter with no subFinal element.
2935 */
2936 public String[] getSubAnyStrings()
2937 {
2938 final String[] subAnyStrings = new String[subAny.length];
2939 for (int i=0; i < subAny.length; i++)
2940 {
2941 subAnyStrings[i] = subAny[i].stringValue();
2942 }
2943
2944 return subAnyStrings;
2945 }
2946
2947
2948
2949 /**
2950 * Retrieves the binary representations of the subAny elements for this
2951 * substring filter. This is not applicable for any other filter type.
2952 *
2953 * @return The binary representations of the subAny elements for this
2954 * substring filter, or an empty array if this is some other type of
2955 * filter, or if it is a substring filter with no subFinal element.
2956 */
2957 public byte[][] getSubAnyBytes()
2958 {
2959 final byte[][] subAnyBytes = new byte[subAny.length][];
2960 for (int i=0; i < subAny.length; i++)
2961 {
2962 subAnyBytes[i] = subAny[i].getValue();
2963 }
2964
2965 return subAnyBytes;
2966 }
2967
2968
2969
2970 /**
2971 * Retrieves the raw subAny values for this substring filter. This is not
2972 * applicable for any other filter type.
2973 *
2974 * @return The raw subAny values for this substring filter, or an empty array
2975 * if this is some other type of filter, or if it is a substring
2976 * filter with no subFinal element.
2977 */
2978 public ASN1OctetString[] getRawSubAnyValues()
2979 {
2980 return subAny;
2981 }
2982
2983
2984
2985 /**
2986 * Retrieves the string representation of the subFinal element for this
2987 * substring filter. This is not applicable for any other filter type.
2988 *
2989 * @return The string representation of the subFinal element for this
2990 * substring filter, or {@code null} if this is some other type of
2991 * filter, or if it is a substring filter with no subFinal element.
2992 */
2993 public String getSubFinalString()
2994 {
2995 if (subFinal == null)
2996 {
2997 return null;
2998 }
2999 else
3000 {
3001 return subFinal.stringValue();
3002 }
3003 }
3004
3005
3006
3007 /**
3008 * Retrieves the binary representation of the subFinal element for this
3009 * substring filter. This is not applicable for any other filter type.
3010 *
3011 * @return The binary representation of the subFinal element for this
3012 * substring filter, or {@code null} if this is some other type of
3013 * filter, or if it is a substring filter with no subFinal element.
3014 */
3015 public byte[] getSubFinalBytes()
3016 {
3017 if (subFinal == null)
3018 {
3019 return null;
3020 }
3021 else
3022 {
3023 return subFinal.getValue();
3024 }
3025 }
3026
3027
3028
3029 /**
3030 * Retrieves the raw subFinal element for this filter as an ASN.1 octet
3031 * string. This is not applicable for any other filter type.
3032 *
3033 * @return The raw subFinal element for this filter as an ASN.1 octet
3034 * string, or {@code null} if this is not a substring filter, or if
3035 * it is a substring filter with no subFinal element.
3036 */
3037 public ASN1OctetString getRawSubFinalValue()
3038 {
3039 return subFinal;
3040 }
3041
3042
3043
3044 /**
3045 * Retrieves the matching rule ID for this extensible match filter. This is
3046 * not applicable for any other filter type.
3047 *
3048 * @return The matching rule ID for this extensible match filter, or
3049 * {@code null} if this is some other type of filter, or if this
3050 * extensible match filter does not have a matching rule ID.
3051 */
3052 public String getMatchingRuleID()
3053 {
3054 return matchingRuleID;
3055 }
3056
3057
3058
3059 /**
3060 * Retrieves the dnAttributes flag for this extensible match filter. This is
3061 * not applicable for any other filter type.
3062 *
3063 * @return The dnAttributes flag for this extensible match filter.
3064 */
3065 public boolean getDNAttributes()
3066 {
3067 return dnAttributes;
3068 }
3069
3070
3071
3072 /**
3073 * Indicates whether this filter matches the provided entry. Note that this
3074 * is a best-guess effort and may not be completely accurate in all cases.
3075 * All matching will be performed using case-ignore string matching, which may
3076 * yield an unexpected result for values that should not be treated as simple
3077 * strings. For example:
3078 * <UL>
3079 * <LI>Two DN values which are logically equivalent may not be considered
3080 * matches if they have different spacing.</LI>
3081 * <LI>Ordering comparisons against numeric values may yield unexpected
3082 * results (e.g., "2" will be considered greater than "10" because the
3083 * character "2" has a larger ASCII value than the character "1").</LI>
3084 * </UL>
3085 * <BR>
3086 * In addition to the above constraints, it should be noted that neither
3087 * approximate matching nor extensible matching are currently supported.
3088 *
3089 * @param entry The entry for which to make the determination. It must not
3090 * be {@code null}.
3091 *
3092 * @return {@code true} if this filter appears to match the provided entry,
3093 * or {@code false} if not.
3094 *
3095 * @throws LDAPException If a problem occurs while trying to make the
3096 * determination.
3097 */
3098 public boolean matchesEntry(final Entry entry)
3099 throws LDAPException
3100 {
3101 return matchesEntry(entry, entry.getSchema());
3102 }
3103
3104
3105
3106 /**
3107 * Indicates whether this filter matches the provided entry. Note that this
3108 * is a best-guess effort and may not be completely accurate in all cases.
3109 * If provided, the given schema will be used in an attempt to determine the
3110 * appropriate matching rule for making the determinations, but some corner
3111 * cases may not be handled accurately. Neither approximate matching nor
3112 * extensible matching are currently supported.
3113 *
3114 * @param entry The entry for which to make the determination. It must not
3115 * be {@code null}.
3116 * @param schema The schema to use when making the determination. If this
3117 * is {@code null}, then all matching will be performed using
3118 * a case-ignore matching rule.
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, final Schema schema)
3127 throws LDAPException
3128 {
3129 ensureNotNull(entry);
3130
3131 switch (filterType)
3132 {
3133 case FILTER_TYPE_AND:
3134 for (final Filter f : filterComps)
3135 {
3136 if (! f.matchesEntry(entry, schema))
3137 {
3138 return false;
3139 }
3140 }
3141 return true;
3142
3143 case FILTER_TYPE_OR:
3144 for (final Filter f : filterComps)
3145 {
3146 if (f.matchesEntry(entry, schema))
3147 {
3148 return true;
3149 }
3150 }
3151 return false;
3152
3153 case FILTER_TYPE_NOT:
3154 return (! notComp.matchesEntry(entry, schema));
3155
3156 case FILTER_TYPE_EQUALITY:
3157 Attribute a = entry.getAttribute(attrName, schema);
3158 if (a == null)
3159 {
3160 return false;
3161 }
3162
3163 MatchingRule matchingRule =
3164 MatchingRule.selectEqualityMatchingRule(attrName, schema);
3165 for (final ASN1OctetString v : a.getRawValues())
3166 {
3167 if (matchingRule.valuesMatch(v, assertionValue))
3168 {
3169 return true;
3170 }
3171 }
3172 return false;
3173
3174 case FILTER_TYPE_SUBSTRING:
3175 a = entry.getAttribute(attrName, schema);
3176 if (a == null)
3177 {
3178 return false;
3179 }
3180
3181 matchingRule =
3182 MatchingRule.selectSubstringMatchingRule(attrName, schema);
3183 for (final ASN1OctetString v : a.getRawValues())
3184 {
3185 if (matchingRule.matchesSubstring(v, subInitial, subAny, subFinal))
3186 {
3187 return true;
3188 }
3189 }
3190 return false;
3191
3192 case FILTER_TYPE_GREATER_OR_EQUAL:
3193 a = entry.getAttribute(attrName, schema);
3194 if (a == null)
3195 {
3196 return false;
3197 }
3198
3199 matchingRule =
3200 MatchingRule.selectOrderingMatchingRule(attrName, schema);
3201 for (final ASN1OctetString v : a.getRawValues())
3202 {
3203 if (matchingRule.compareValues(v, assertionValue) >= 0)
3204 {
3205 return true;
3206 }
3207 }
3208 return false;
3209
3210 case FILTER_TYPE_LESS_OR_EQUAL:
3211 a = entry.getAttribute(attrName, schema);
3212 if (a == null)
3213 {
3214 return false;
3215 }
3216
3217 matchingRule =
3218 MatchingRule.selectOrderingMatchingRule(attrName, schema);
3219 for (final ASN1OctetString v : a.getRawValues())
3220 {
3221 if (matchingRule.compareValues(v, assertionValue) <= 0)
3222 {
3223 return true;
3224 }
3225 }
3226 return false;
3227
3228 case FILTER_TYPE_PRESENCE:
3229 return (entry.hasAttribute(attrName));
3230
3231 case FILTER_TYPE_APPROXIMATE_MATCH:
3232 throw new LDAPException(ResultCode.NOT_SUPPORTED,
3233 ERR_FILTER_APPROXIMATE_MATCHING_NOT_SUPPORTED.get());
3234
3235 case FILTER_TYPE_EXTENSIBLE_MATCH:
3236 throw new LDAPException(ResultCode.NOT_SUPPORTED,
3237 ERR_FILTER_EXTENSIBLE_MATCHING_NOT_SUPPORTED.get());
3238
3239 default:
3240 throw new LDAPException(ResultCode.PARAM_ERROR,
3241 ERR_FILTER_INVALID_TYPE.get());
3242 }
3243 }
3244
3245
3246
3247 /**
3248 * Generates a hash code for this search filter.
3249 *
3250 * @return The generated hash code for this search filter.
3251 */
3252 @Override()
3253 public int hashCode()
3254 {
3255 final CaseIgnoreStringMatchingRule matchingRule =
3256 CaseIgnoreStringMatchingRule.getInstance();
3257 int hashCode = filterType;
3258
3259 switch (filterType)
3260 {
3261 case FILTER_TYPE_AND:
3262 case FILTER_TYPE_OR:
3263 for (final Filter f : filterComps)
3264 {
3265 hashCode += f.hashCode();
3266 }
3267 break;
3268
3269 case FILTER_TYPE_NOT:
3270 hashCode += notComp.hashCode();
3271 break;
3272
3273 case FILTER_TYPE_EQUALITY:
3274 case FILTER_TYPE_GREATER_OR_EQUAL:
3275 case FILTER_TYPE_LESS_OR_EQUAL:
3276 case FILTER_TYPE_APPROXIMATE_MATCH:
3277 hashCode += toLowerCase(attrName).hashCode();
3278 hashCode += matchingRule.normalize(assertionValue).hashCode();
3279 break;
3280
3281 case FILTER_TYPE_SUBSTRING:
3282 hashCode += toLowerCase(attrName).hashCode();
3283 if (subInitial != null)
3284 {
3285 hashCode += matchingRule.normalizeSubstring(subInitial,
3286 MatchingRule.SUBSTRING_TYPE_SUBINITIAL).hashCode();
3287 }
3288 for (final ASN1OctetString s : subAny)
3289 {
3290 hashCode += matchingRule.normalizeSubstring(s,
3291 MatchingRule.SUBSTRING_TYPE_SUBANY).hashCode();
3292 }
3293 if (subFinal != null)
3294 {
3295 hashCode += matchingRule.normalizeSubstring(subFinal,
3296 MatchingRule.SUBSTRING_TYPE_SUBFINAL).hashCode();
3297 }
3298 break;
3299
3300 case FILTER_TYPE_PRESENCE:
3301 hashCode += toLowerCase(attrName).hashCode();
3302 break;
3303
3304 case FILTER_TYPE_EXTENSIBLE_MATCH:
3305 if (attrName != null)
3306 {
3307 hashCode += toLowerCase(attrName).hashCode();
3308 }
3309
3310 if (matchingRuleID != null)
3311 {
3312 hashCode += toLowerCase(matchingRuleID).hashCode();
3313 }
3314
3315 if (dnAttributes)
3316 {
3317 hashCode++;
3318 }
3319
3320 hashCode += matchingRule.normalize(assertionValue).hashCode();
3321 break;
3322 }
3323
3324 return hashCode;
3325 }
3326
3327
3328
3329 /**
3330 * Indicates whether the provided object is equal to this search filter.
3331 *
3332 * @param o The object for which to make the determination.
3333 *
3334 * @return {@code true} if the provided object can be considered equal to
3335 * this search filter, or {@code false} if not.
3336 */
3337 @Override()
3338 public boolean equals(final Object o)
3339 {
3340 if (o == null)
3341 {
3342 return false;
3343 }
3344
3345 if (o == this)
3346 {
3347 return true;
3348 }
3349
3350 if (! (o instanceof Filter))
3351 {
3352 return false;
3353 }
3354
3355 final Filter f = (Filter) o;
3356 if (filterType != f.filterType)
3357 {
3358 return false;
3359 }
3360
3361 final CaseIgnoreStringMatchingRule matchingRule =
3362 CaseIgnoreStringMatchingRule.getInstance();
3363
3364 switch (filterType)
3365 {
3366 case FILTER_TYPE_AND:
3367 case FILTER_TYPE_OR:
3368 if (filterComps.length != f.filterComps.length)
3369 {
3370 return false;
3371 }
3372
3373 final HashSet<Filter> compSet = new HashSet<Filter>();
3374 compSet.addAll(Arrays.asList(filterComps));
3375
3376 for (final Filter filterComp : f.filterComps)
3377 {
3378 if (! compSet.remove(filterComp))
3379 {
3380 return false;
3381 }
3382 }
3383
3384 return true;
3385
3386
3387 case FILTER_TYPE_NOT:
3388 return notComp.equals(f.notComp);
3389
3390
3391 case FILTER_TYPE_EQUALITY:
3392 case FILTER_TYPE_GREATER_OR_EQUAL:
3393 case FILTER_TYPE_LESS_OR_EQUAL:
3394 case FILTER_TYPE_APPROXIMATE_MATCH:
3395 return (attrName.equalsIgnoreCase(f.attrName) &&
3396 matchingRule.valuesMatch(assertionValue, f.assertionValue));
3397
3398
3399 case FILTER_TYPE_SUBSTRING:
3400 if (! attrName.equalsIgnoreCase(f.attrName))
3401 {
3402 return false;
3403 }
3404
3405 if (subAny.length != f.subAny.length)
3406 {
3407 return false;
3408 }
3409
3410 if (subInitial == null)
3411 {
3412 if (f.subInitial != null)
3413 {
3414 return false;
3415 }
3416 }
3417 else
3418 {
3419 if (f.subInitial == null)
3420 {
3421 return false;
3422 }
3423
3424 final ASN1OctetString si1 = matchingRule.normalizeSubstring(
3425 subInitial, MatchingRule.SUBSTRING_TYPE_SUBINITIAL);
3426 final ASN1OctetString si2 = matchingRule.normalizeSubstring(
3427 f.subInitial, MatchingRule.SUBSTRING_TYPE_SUBINITIAL);
3428 if (! si1.equals(si2))
3429 {
3430 return false;
3431 }
3432 }
3433
3434 for (int i=0; i < subAny.length; i++)
3435 {
3436 final ASN1OctetString sa1 = matchingRule.normalizeSubstring(subAny[i],
3437 MatchingRule.SUBSTRING_TYPE_SUBANY);
3438 final ASN1OctetString sa2 = matchingRule.normalizeSubstring(
3439 f.subAny[i], MatchingRule.SUBSTRING_TYPE_SUBANY);
3440 if (! sa1.equals(sa2))
3441 {
3442 return false;
3443 }
3444 }
3445
3446 if (subFinal == null)
3447 {
3448 if (f.subFinal != null)
3449 {
3450 return false;
3451 }
3452 }
3453 else
3454 {
3455 if (f.subFinal == null)
3456 {
3457 return false;
3458 }
3459
3460 final ASN1OctetString sf1 = matchingRule.normalizeSubstring(subFinal,
3461 MatchingRule.SUBSTRING_TYPE_SUBFINAL);
3462 final ASN1OctetString sf2 = matchingRule.normalizeSubstring(
3463 f.subFinal, MatchingRule.SUBSTRING_TYPE_SUBFINAL);
3464 if (! sf1.equals(sf2))
3465 {
3466 return false;
3467 }
3468 }
3469
3470 return true;
3471
3472
3473 case FILTER_TYPE_PRESENCE:
3474 return (attrName.equalsIgnoreCase(f.attrName));
3475
3476
3477 case FILTER_TYPE_EXTENSIBLE_MATCH:
3478 if (attrName == null)
3479 {
3480 if (f.attrName != null)
3481 {
3482 return false;
3483 }
3484 }
3485 else
3486 {
3487 if (f.attrName == null)
3488 {
3489 return false;
3490 }
3491 else
3492 {
3493 if (! attrName.equalsIgnoreCase(f.attrName))
3494 {
3495 return false;
3496 }
3497 }
3498 }
3499
3500 if (matchingRuleID == null)
3501 {
3502 if (f.matchingRuleID != null)
3503 {
3504 return false;
3505 }
3506 }
3507 else
3508 {
3509 if (f.matchingRuleID == null)
3510 {
3511 return false;
3512 }
3513 else
3514 {
3515 if (! matchingRuleID.equalsIgnoreCase(f.matchingRuleID))
3516 {
3517 return false;
3518 }
3519 }
3520 }
3521
3522 if (dnAttributes != f.dnAttributes)
3523 {
3524 return false;
3525 }
3526
3527 return matchingRule.valuesMatch(assertionValue, f.assertionValue);
3528
3529
3530 default:
3531 return false;
3532 }
3533 }
3534
3535
3536
3537 /**
3538 * Retrieves a string representation of this search filter.
3539 *
3540 * @return A string representation of this search filter.
3541 */
3542 @Override()
3543 public String toString()
3544 {
3545 if (filterString == null)
3546 {
3547 final StringBuilder buffer = new StringBuilder();
3548 toString(buffer);
3549 filterString = buffer.toString();
3550 }
3551
3552 return filterString;
3553 }
3554
3555
3556
3557 /**
3558 * Appends a string representation of this search filter to the provided
3559 * buffer.
3560 *
3561 * @param buffer The buffer to which to append a string representation of
3562 * this search filter.
3563 */
3564 public void toString(final StringBuilder buffer)
3565 {
3566 switch (filterType)
3567 {
3568 case FILTER_TYPE_AND:
3569 buffer.append("(&");
3570 for (final Filter f : filterComps)
3571 {
3572 f.toString(buffer);
3573 }
3574 buffer.append(')');
3575 break;
3576
3577 case FILTER_TYPE_OR:
3578 buffer.append("(|");
3579 for (final Filter f : filterComps)
3580 {
3581 f.toString(buffer);
3582 }
3583 buffer.append(')');
3584 break;
3585
3586 case FILTER_TYPE_NOT:
3587 buffer.append("(!");
3588 notComp.toString(buffer);
3589 buffer.append(')');
3590 break;
3591
3592 case FILTER_TYPE_EQUALITY:
3593 buffer.append('(');
3594 buffer.append(attrName);
3595 buffer.append('=');
3596 encodeValue(assertionValue, buffer);
3597 buffer.append(')');
3598 break;
3599
3600 case FILTER_TYPE_SUBSTRING:
3601 buffer.append('(');
3602 buffer.append(attrName);
3603 buffer.append('=');
3604 if (subInitial != null)
3605 {
3606 encodeValue(subInitial, buffer);
3607 }
3608 buffer.append('*');
3609 for (final ASN1OctetString s : subAny)
3610 {
3611 encodeValue(s, buffer);
3612 buffer.append('*');
3613 }
3614 if (subFinal != null)
3615 {
3616 encodeValue(subFinal, buffer);
3617 }
3618 buffer.append(')');
3619 break;
3620
3621 case FILTER_TYPE_GREATER_OR_EQUAL:
3622 buffer.append('(');
3623 buffer.append(attrName);
3624 buffer.append(">=");
3625 encodeValue(assertionValue, buffer);
3626 buffer.append(')');
3627 break;
3628
3629 case FILTER_TYPE_LESS_OR_EQUAL:
3630 buffer.append('(');
3631 buffer.append(attrName);
3632 buffer.append("<=");
3633 encodeValue(assertionValue, buffer);
3634 buffer.append(')');
3635 break;
3636
3637 case FILTER_TYPE_PRESENCE:
3638 buffer.append('(');
3639 buffer.append(attrName);
3640 buffer.append("=*)");
3641 break;
3642
3643 case FILTER_TYPE_APPROXIMATE_MATCH:
3644 buffer.append('(');
3645 buffer.append(attrName);
3646 buffer.append("~=");
3647 encodeValue(assertionValue, buffer);
3648 buffer.append(')');
3649 break;
3650
3651 case FILTER_TYPE_EXTENSIBLE_MATCH:
3652 buffer.append('(');
3653 if (attrName != null)
3654 {
3655 buffer.append(attrName);
3656 }
3657
3658 if (dnAttributes)
3659 {
3660 buffer.append(":dn");
3661 }
3662
3663 if (matchingRuleID != null)
3664 {
3665 buffer.append(':');
3666 buffer.append(matchingRuleID);
3667 }
3668
3669 buffer.append(":=");
3670 encodeValue(assertionValue, buffer);
3671 buffer.append(')');
3672 break;
3673 }
3674 }
3675
3676
3677
3678 /**
3679 * Retrieves a normalized string representation of this search filter.
3680 *
3681 * @return A normalized string representation of this search filter.
3682 */
3683 public String toNormalizedString()
3684 {
3685 if (normalizedString == null)
3686 {
3687 final StringBuilder buffer = new StringBuilder();
3688 toNormalizedString(buffer);
3689 normalizedString = buffer.toString();
3690 }
3691
3692 return normalizedString;
3693 }
3694
3695
3696
3697 /**
3698 * Appends a normalized string representation of this search filter to the
3699 * provided buffer.
3700 *
3701 * @param buffer The buffer to which to append a normalized string
3702 * representation of this search filter.
3703 */
3704 public void toNormalizedString(final StringBuilder buffer)
3705 {
3706 final CaseIgnoreStringMatchingRule mr =
3707 CaseIgnoreStringMatchingRule.getInstance();
3708
3709 switch (filterType)
3710 {
3711 case FILTER_TYPE_AND:
3712 buffer.append("(&");
3713 for (final Filter f : filterComps)
3714 {
3715 f.toNormalizedString(buffer);
3716 }
3717 buffer.append(')');
3718 break;
3719
3720 case FILTER_TYPE_OR:
3721 buffer.append("(|");
3722 for (final Filter f : filterComps)
3723 {
3724 f.toNormalizedString(buffer);
3725 }
3726 buffer.append(')');
3727 break;
3728
3729 case FILTER_TYPE_NOT:
3730 buffer.append("(!");
3731 notComp.toNormalizedString(buffer);
3732 buffer.append(')');
3733 break;
3734
3735 case FILTER_TYPE_EQUALITY:
3736 buffer.append('(');
3737 buffer.append(toLowerCase(attrName));
3738 buffer.append('=');
3739 encodeValue(mr.normalize(assertionValue), buffer);
3740 buffer.append(')');
3741 break;
3742
3743 case FILTER_TYPE_SUBSTRING:
3744 buffer.append('(');
3745 buffer.append(toLowerCase(attrName));
3746 buffer.append('=');
3747 if (subInitial != null)
3748 {
3749 encodeValue(mr.normalizeSubstring(subInitial,
3750 MatchingRule.SUBSTRING_TYPE_SUBINITIAL), buffer);
3751 }
3752 buffer.append('*');
3753 for (final ASN1OctetString s : subAny)
3754 {
3755 encodeValue(mr.normalizeSubstring(s,
3756 MatchingRule.SUBSTRING_TYPE_SUBANY), buffer);
3757 buffer.append('*');
3758 }
3759 if (subFinal != null)
3760 {
3761 encodeValue(mr.normalizeSubstring(subFinal,
3762 MatchingRule.SUBSTRING_TYPE_SUBFINAL), buffer);
3763 }
3764 buffer.append(')');
3765 break;
3766
3767 case FILTER_TYPE_GREATER_OR_EQUAL:
3768 buffer.append('(');
3769 buffer.append(toLowerCase(attrName));
3770 buffer.append(">=");
3771 encodeValue(mr.normalize(assertionValue), buffer);
3772 buffer.append(')');
3773 break;
3774
3775 case FILTER_TYPE_LESS_OR_EQUAL:
3776 buffer.append('(');
3777 buffer.append(toLowerCase(attrName));
3778 buffer.append("<=");
3779 encodeValue(mr.normalize(assertionValue), buffer);
3780 buffer.append(')');
3781 break;
3782
3783 case FILTER_TYPE_PRESENCE:
3784 buffer.append('(');
3785 buffer.append(toLowerCase(attrName));
3786 buffer.append("=*)");
3787 break;
3788
3789 case FILTER_TYPE_APPROXIMATE_MATCH:
3790 buffer.append('(');
3791 buffer.append(toLowerCase(attrName));
3792 buffer.append("~=");
3793 encodeValue(mr.normalize(assertionValue), buffer);
3794 buffer.append(')');
3795 break;
3796
3797 case FILTER_TYPE_EXTENSIBLE_MATCH:
3798 buffer.append('(');
3799 if (attrName != null)
3800 {
3801 buffer.append(toLowerCase(attrName));
3802 }
3803
3804 if (dnAttributes)
3805 {
3806 buffer.append(":dn");
3807 }
3808
3809 if (matchingRuleID != null)
3810 {
3811 buffer.append(':');
3812 buffer.append(toLowerCase(matchingRuleID));
3813 }
3814
3815 buffer.append(":=");
3816 encodeValue(mr.normalize(assertionValue), buffer);
3817 buffer.append(')');
3818 break;
3819 }
3820 }
3821
3822
3823
3824 /**
3825 * Encodes the provided value into a form suitable for use as the assertion
3826 * value in the string representation of a search filter. Parentheses,
3827 * asterisks, backslashes, null characters, and any non-ASCII characters will
3828 * be escaped using a backslash before the hexadecimal representation of each
3829 * byte in the character to escape.
3830 *
3831 * @param value The value to be encoded. It must not be {@code null}.
3832 *
3833 * @return The encoded representation of the provided string.
3834 */
3835 public static String encodeValue(final String value)
3836 {
3837 ensureNotNull(value);
3838
3839 final StringBuilder buffer = new StringBuilder();
3840 encodeValue(new ASN1OctetString(value), buffer);
3841 return buffer.toString();
3842 }
3843
3844
3845
3846 /**
3847 * Encodes the provided value into a form suitable for use as the assertion
3848 * value in the string representation of a search filter. Parentheses,
3849 * asterisks, backslashes, null characters, and any non-ASCII characters will
3850 * be escaped using a backslash before the hexadecimal representation of each
3851 * byte in the character to escape.
3852 *
3853 * @param value The value to be encoded. It must not be {@code null}.
3854 *
3855 * @return The encoded representation of the provided string.
3856 */
3857 public static String encodeValue(final byte[]value)
3858 {
3859 ensureNotNull(value);
3860
3861 final StringBuilder buffer = new StringBuilder();
3862 encodeValue(new ASN1OctetString(value), buffer);
3863 return buffer.toString();
3864 }
3865
3866
3867
3868 /**
3869 * Appends the assertion value for this filter to the provided buffer,
3870 * encoding any special characters as necessary.
3871 *
3872 * @param value The value to be encoded.
3873 * @param buffer The buffer to which the assertion value should be appended.
3874 */
3875 private static void encodeValue(final ASN1OctetString value,
3876 final StringBuilder buffer)
3877 {
3878 final byte[] valueBytes = value.getValue();
3879 for (int i=0; i < valueBytes.length; i++)
3880 {
3881 switch (numBytesInUTF8CharacterWithFirstByte(valueBytes[i]))
3882 {
3883 case 1:
3884 // This character is ASCII, but might still need to be escaped. We'll
3885 // escape anything
3886 if ((valueBytes[i] <= 0x1F) || // Non-printable ASCII characters.
3887 (valueBytes[i] == 0x28) || // Open parenthesis
3888 (valueBytes[i] == 0x29) || // Close parenthesis
3889 (valueBytes[i] == 0x2A) || // Asterisk
3890 (valueBytes[i] == 0x5C) || // Backslash
3891 (valueBytes[i] == 0x7F)) // DEL
3892 {
3893 buffer.append('\\');
3894 toHex(valueBytes[i], buffer);
3895 }
3896 else
3897 {
3898 buffer.append((char) valueBytes[i]);
3899 }
3900 break;
3901
3902 case 2:
3903 // If there are at least two bytes left, then we'll hex-encode the
3904 // next two bytes. Otherwise we'll hex-encode whatever is left.
3905 buffer.append('\\');
3906 toHex(valueBytes[i++], buffer);
3907 if (i < valueBytes.length)
3908 {
3909 buffer.append('\\');
3910 toHex(valueBytes[i], buffer);
3911 }
3912 break;
3913
3914 case 3:
3915 // If there are at least three bytes left, then we'll hex-encode the
3916 // next three bytes. Otherwise we'll hex-encode whatever is left.
3917 buffer.append('\\');
3918 toHex(valueBytes[i++], buffer);
3919 if (i < valueBytes.length)
3920 {
3921 buffer.append('\\');
3922 toHex(valueBytes[i++], buffer);
3923 }
3924 if (i < valueBytes.length)
3925 {
3926 buffer.append('\\');
3927 toHex(valueBytes[i], buffer);
3928 }
3929 break;
3930
3931 case 4:
3932 // If there are at least four bytes left, then we'll hex-encode the
3933 // next four bytes. Otherwise we'll hex-encode whatever is left.
3934 buffer.append('\\');
3935 toHex(valueBytes[i++], buffer);
3936 if (i < valueBytes.length)
3937 {
3938 buffer.append('\\');
3939 toHex(valueBytes[i++], buffer);
3940 }
3941 if (i < valueBytes.length)
3942 {
3943 buffer.append('\\');
3944 toHex(valueBytes[i++], buffer);
3945 }
3946 if (i < valueBytes.length)
3947 {
3948 buffer.append('\\');
3949 toHex(valueBytes[i], buffer);
3950 }
3951 break;
3952
3953 default:
3954 // We'll hex-encode whatever is left in the buffer.
3955 while (i < valueBytes.length)
3956 {
3957 buffer.append('\\');
3958 toHex(valueBytes[i++], buffer);
3959 }
3960 break;
3961 }
3962 }
3963 }
3964 }