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