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