001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.types;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.util.ArrayList;
033    
034    import org.opends.server.protocols.asn1.ASN1Boolean;
035    import org.opends.server.protocols.asn1.ASN1Element;
036    import org.opends.server.protocols.asn1.ASN1OctetString;
037    import org.opends.server.protocols.asn1.ASN1Sequence;
038    import org.opends.server.protocols.asn1.ASN1Set;
039    import org.opends.server.protocols.ldap.LDAPFilter;
040    
041    import static org.opends.server.loggers.debug.DebugLogger.*;
042    import org.opends.server.loggers.debug.DebugTracer;
043    import static org.opends.messages.ProtocolMessages.*;
044    import static org.opends.server.protocols.ldap.LDAPConstants.*;
045    import static org.opends.server.protocols.ldap.LDAPResultCode.*;
046    import static org.opends.server.util.StaticUtils.*;
047    
048    
049    
050    /**
051     * This class defines the data structures and methods to use when
052     * interacting with a raw search filter, which defines a set of
053     * criteria for locating entries in a search request.
054     */
055    @org.opends.server.types.PublicAPI(
056         stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
057         mayInstantiate=true,
058         mayExtend=false,
059         mayInvoke=true)
060    public abstract class RawFilter
061    {
062      /**
063       * The tracer object for the debug logger.
064       */
065      private static final DebugTracer TRACER = getTracer();
066    
067      /**
068       * Creates a new LDAP filter from the provided filter string.
069       *
070       * @param  filterString  The filter string to use to create this raw
071       *                       filter.
072       *
073       * @return  The raw filter decoded from the provided filter string.
074       *
075       * @throws  LDAPException  If the provied filter string could not be
076       *                         decoded as a raw filter.
077       */
078      public static RawFilter create(String filterString)
079             throws LDAPException
080      {
081        return LDAPFilter.decode(filterString);
082      }
083    
084    
085    
086      /**
087       * Creates a new LDAP filter from the provided search filter.
088       *
089       * @param  filter  The search filter to use to create this raw
090       *                 filter.
091       *
092       * @return  The constructed raw filter.
093       */
094      public static RawFilter create(SearchFilter filter)
095      {
096        return new LDAPFilter(filter);
097      }
098    
099    
100    
101      /**
102       * Creates a new AND search filter with the provided filter
103       * components.
104       *
105       * @param  filterComponents  The filter components for this AND
106       *                           filter.
107       *
108       * @return  The AND search filter with the provided filter
109       *          components.
110       */
111      public static LDAPFilter createANDFilter(ArrayList<RawFilter>
112                                                    filterComponents)
113      {
114        return new LDAPFilter(FilterType.AND, filterComponents, null,
115                              null, null, null, null, null, null, false);
116      }
117    
118    
119    
120      /**
121       * Creates a new OR search filter with the provided filter
122       * components.
123       *
124       * @param  filterComponents  The filter components for this OR
125       *                           filter.
126       *
127       * @return  The OR search filter with the provided filter
128       *          components.
129       */
130      public static LDAPFilter createORFilter(ArrayList<RawFilter>
131                                                   filterComponents)
132      {
133        return new LDAPFilter(FilterType.OR, filterComponents, null, null,
134                              null, null, null, null, null, false);
135      }
136    
137    
138    
139      /**
140       * Creates a new NOT search filter with the provided filter
141       * component.
142       *
143       * @param  notComponent  The filter component for this NOT filter.
144       *
145       * @return  The NOT search filter with the provided filter
146       *          component.
147       */
148      public static LDAPFilter createNOTFilter(RawFilter notComponent)
149      {
150        return new LDAPFilter(FilterType.NOT, null, notComponent, null,
151                              null, null, null, null, null, false);
152      }
153    
154    
155    
156      /**
157       * Creates a new equality search filter with the provided
158       * information.
159       *
160       * @param  attributeType   The attribute type for this equality
161       *                         filter.
162       * @param  assertionValue  The assertion value for this equality
163       *                         filter.
164       *
165       * @return  The constructed equality search filter.
166       */
167      public static LDAPFilter createEqualityFilter(String attributeType,
168                                    ByteString assertionValue)
169      {
170        return new LDAPFilter(FilterType.EQUALITY, null, null,
171                              attributeType, assertionValue, null, null,
172                              null, null, false);
173      }
174    
175    
176    
177      /**
178       * Creates a new substring search filter with the provided
179       * information.
180       *
181       * @param  attributeType      The attribute type for this substring
182       *                            filter.
183       * @param  subInitialElement  The subInitial element for this
184       *                            substring filter.
185       * @param  subAnyElements     The subAny elements for this substring
186       *                            filter.
187       * @param  subFinalElement    The subFinal element for this
188       *                            substring filter.
189       *
190       * @return  The constructed substring search filter.
191       */
192      public static LDAPFilter createSubstringFilter(String attributeType,
193                                    ByteString subInitialElement,
194                                    ArrayList<ByteString> subAnyElements,
195                                    ByteString subFinalElement)
196      {
197        return new LDAPFilter(FilterType.SUBSTRING, null, null,
198                              attributeType, null, subInitialElement,
199                              subAnyElements, subFinalElement, null,
200                              false);
201      }
202    
203    
204    
205      /**
206       * Creates a new greater or equal search filter with the provided
207       * information.
208       *
209       * @param  attributeType   The attribute type for this greater or
210       *                         equal filter.
211       * @param  assertionValue  The assertion value for this greater or
212       *                         equal filter.
213       *
214       * @return  The constructed greater or equal search filter.
215       */
216      public static LDAPFilter createGreaterOrEqualFilter(
217                                    String attributeType,
218                                    ByteString assertionValue)
219      {
220        return new LDAPFilter(FilterType.GREATER_OR_EQUAL, null, null,
221                              attributeType, assertionValue, null, null,
222                              null, null, false);
223      }
224    
225    
226    
227      /**
228       * Creates a new less or equal search filter with the provided
229       * information.
230       *
231       * @param  attributeType   The attribute type for this less or equal
232       *                         filter.
233       * @param  assertionValue  The assertion value for this less or
234       *                         equal filter.
235       *
236       * @return  The constructed less or equal search filter.
237       */
238      public static LDAPFilter createLessOrEqualFilter(
239                                    String attributeType,
240                                    ByteString assertionValue)
241      {
242        return new LDAPFilter(FilterType.LESS_OR_EQUAL, null, null,
243                              attributeType, assertionValue, null, null,
244                              null, null, false);
245      }
246    
247    
248    
249      /**
250       * Creates a new presence search filter with the provided attribute
251       * type.
252       *
253       * @param  attributeType  The attribute type for this presence
254       *                        filter.
255       *
256       * @return  The constructed presence search filter.
257       */
258      public static LDAPFilter createPresenceFilter(String attributeType)
259      {
260        return new LDAPFilter(FilterType.PRESENT, null, null,
261                              attributeType, null, null, null, null, null,
262                              false);
263      }
264    
265    
266    
267      /**
268       * Creates a new approximate search filter with the provided
269       * information.
270       *
271       * @param  attributeType   The attribute type for this approximate
272       *                         filter.
273       * @param  assertionValue  The assertion value for this approximate
274       *                         filter.
275       *
276       * @return  The constructed approximate search filter.
277       */
278      public static LDAPFilter createApproximateFilter(
279                                    String attributeType,
280                                    ByteString assertionValue)
281      {
282        return new LDAPFilter(FilterType.APPROXIMATE_MATCH, null, null,
283                              attributeType, assertionValue, null, null,
284                              null, null, false);
285      }
286    
287    
288    
289      /**
290       * Creates a new extensible matching search filter with the provided
291       * information.
292       *
293       * @param  matchingRuleID  The matching rule ID for this extensible
294       *                         filter.
295       * @param  attributeType   The attribute type for this extensible
296       *                         filter.
297       * @param  assertionValue  The assertion value for this extensible
298       *                         filter.
299       * @param  dnAttributes    The dnAttributes flag for this extensible
300       *                         filter.
301       *
302       * @return  The constructed extensible matching search filter.
303       */
304      public static LDAPFilter createExtensibleFilter(
305                                    String matchingRuleID,
306                                    String attributeType,
307                                    ByteString assertionValue,
308                                    boolean dnAttributes)
309      {
310        return new LDAPFilter(FilterType.EXTENSIBLE_MATCH, null, null,
311                              attributeType, assertionValue, null, null,
312                              null, matchingRuleID, dnAttributes);
313      }
314    
315    
316    
317      /**
318       * Retrieves the filter type for this search filter.
319       *
320       * @return  The filter type for this search filter.
321       */
322      public abstract FilterType getFilterType();
323    
324    
325    
326      /**
327       * Retrieves the set of subordinate filter components for AND or OR
328       * searches.  The contents of the returned list may be altered by
329       * the caller.
330       *
331       * @return  The set of subordinate filter components for AND and OR
332       *          searches, or {@code null} if this is not an AND or OR
333       *          search.
334       */
335      public abstract ArrayList<RawFilter> getFilterComponents();
336    
337    
338    
339      /**
340       * Specifies the set of subordinate filter components for AND or OR
341       * searches.  This will be ignored for all other filter types.
342       *
343       * @param  filterComponents  The set of subordinate filter
344       *                           components for AND or OR searches.
345       */
346      public abstract void setFilterComponents(
347                                ArrayList<RawFilter> filterComponents);
348    
349    
350    
351      /**
352       * Retrieves the subordinate filter component for NOT searches.
353       *
354       * @return  The subordinate filter component for NOT searches, or
355       *          {@code null} if this is not a NOT search.
356       */
357      public abstract RawFilter getNOTComponent();
358    
359    
360    
361      /**
362       * Specifies the subordinate filter component for NOT searches.
363       * This will be ignored for any other type of search.
364       *
365       * @param  notComponent  The subordinate filter component for NOT
366       *                       searches.
367       */
368      public abstract void setNOTComponent(RawFilter notComponent);
369    
370    
371    
372      /**
373       * Retrieves the attribute type for this search filter.  This will
374       * not be applicable for AND, OR, or NOT filters.
375       *
376       * @return  The attribute type for this search filter, or
377       *          {@code null} if there is none.
378       */
379      public abstract String getAttributeType();
380    
381    
382    
383      /**
384       * Specifies the attribute type for this search filter.  This will
385       * be ignored for AND, OR, and NOT searches.
386       *
387       * @param  attributeType  The attribute type for this search filter.
388       */
389      public abstract void setAttributeType(String attributeType);
390    
391    
392    
393      /**
394       * Retrieves the assertion value for this search filter.  This will
395       * only be applicable for equality, greater or equal, less or equal,
396       * approximate, or extensible matching filters.
397       *
398       * @return  The assertion value for this search filter, or
399       *          {@code null} if there is none.
400       */
401      public abstract ByteString getAssertionValue();
402    
403    
404    
405      /**
406       * Specifies the assertion value for this search filter.  This will
407       * be ignored for types of filters that do not have an assertion
408       * value.
409       *
410       * @param  assertionValue  The assertion value for this search
411       *                         filter.
412       */
413      public abstract void setAssertionValue(ByteString assertionValue);
414    
415    
416    
417      /**
418       * Retrieves the subInitial component for this substring filter.
419       * This is only applicable for substring search filters, but even
420       * substring filters might not have a value for this component.
421       *
422       * @return  The subInitial component for this substring filter, or
423       *          {@code null} if there is none.
424       */
425      public abstract ByteString getSubInitialElement();
426    
427    
428    
429      /**
430       * Specifies the subInitial element for this substring filter.
431       * This will be ignored for all other types of filters.
432       *
433       * @param  subInitialElement  The subInitial element for this
434       *                            substring filter.
435       */
436      public abstract void setSubInitialElement(
437                                ByteString subInitialElement);
438    
439    
440    
441      /**
442       * Retrieves the set of subAny elements for this substring filter.
443       * This is only applicable for substring search filters, and even
444       * then may be {@code null} or empty for some substring filters.
445       *
446       * @return  The set of subAny elements for this substring filter, or
447       *          {@code null} if there are none.
448       */
449      public abstract ArrayList<ByteString> getSubAnyElements();
450    
451    
452    
453      /**
454       * Specifies the set of subAny values for this substring filter.
455       * This will be ignored for other filter types.
456       *
457       * @param  subAnyElements  The set of subAny elements for this
458       *                         substring filter.
459       */
460      public abstract void setSubAnyElements(ArrayList<ByteString>
461                                                  subAnyElements);
462    
463    
464    
465      /**
466       * Retrieves the subFinal element for this substring filter.  This
467       * is not applicable for any other filter type, and may not be
468       * provided even for some substring filters.
469       *
470       * @return  The subFinal element for this substring filter, or
471       *          {@code null} if there is none.
472       */
473      public abstract ByteString getSubFinalElement();
474    
475    
476    
477      /**
478       * Specifies the subFinal element for this substring filter.  This
479       * will be ignored for all other filter types.
480       *
481       * @param  subFinalElement  The subFinal element for this substring
482       *                          filter.
483       */
484      public abstract void setSubFinalElement(ByteString subFinalElement);
485    
486    
487    
488      /**
489       * Retrieves the matching rule ID for this extensible match filter.
490       * This is not applicable for any other type of filter and may not
491       * be included in some extensible matching filters.
492       *
493       * @return  The matching rule ID for this extensible match filter,
494       *          or {@code null} if there is none.
495       */
496      public abstract String getMatchingRuleID();
497    
498    
499    
500      /**
501       * Specifies the matching rule ID for this extensible match filter.
502       * It will be ignored for all other filter types.
503       *
504       * @param  matchingRuleID  The matching rule ID for this extensible
505       *                         match filter.
506       */
507      public abstract void setMatchingRuleID(String matchingRuleID);
508    
509    
510    
511      /**
512       * Retrieves the value of the DN attributes flag for this extensible
513       * match filter, which indicates whether to perform matching on the
514       * components of the DN.  This does not apply for any other type of
515       * filter.
516       *
517       * @return  The value of the DN attributes flag for this
518       *          extensibleMatch filter.
519       */
520      public abstract boolean getDNAttributes();
521    
522    
523    
524      /**
525       * Specifies the value of the DN attributes flag for this extensible
526       * match filter.  It will be ignored for all other filter types.
527       *
528       * @param  dnAttributes  The value of the DN attributes flag for
529       *                       this extensible match filter.
530       */
531      public abstract void setDNAttributes(boolean dnAttributes);
532    
533    
534    
535      /**
536       * Encodes this search filter to an ASN.1 element.
537       *
538       * @return  The ASN.1 element containing the encoded search filter.
539       */
540      public final ASN1Element encode()
541      {
542        FilterType filterType = getFilterType();
543        switch (filterType)
544        {
545          case AND:
546          case OR:
547            ArrayList<RawFilter> filterComponents = getFilterComponents();
548            ArrayList<ASN1Element> elements =
549                 new ArrayList<ASN1Element>(filterComponents.size());
550            for (RawFilter f : filterComponents)
551            {
552              elements.add(f.encode());
553            }
554            return new ASN1Set(filterType.getBERType(), elements);
555          case NOT:
556            return new ASN1Element(filterType.getBERType(),
557                                   getNOTComponent().encode().encode());
558          case EQUALITY:
559          case GREATER_OR_EQUAL:
560          case LESS_OR_EQUAL:
561          case APPROXIMATE_MATCH:
562            String attributeType = getAttributeType();
563            ByteString assertionValue = getAssertionValue();
564            elements = new ArrayList<ASN1Element>(2);
565            elements.add(new ASN1OctetString(attributeType));
566            elements.add(assertionValue.toASN1OctetString());
567            return new ASN1Sequence(filterType.getBERType(), elements);
568          case SUBSTRING:
569            attributeType = getAttributeType();
570            elements = new ArrayList<ASN1Element>(2);
571            elements.add(new ASN1OctetString(attributeType));
572    
573            ByteString subInitialElement = getSubInitialElement();
574            ArrayList<ASN1Element> subElements =
575                 new ArrayList<ASN1Element>();
576            if (subInitialElement != null)
577            {
578              ASN1OctetString subInitialOS =
579                   subInitialElement.toASN1OctetString();
580              subInitialOS.setType(TYPE_SUBINITIAL);
581              subElements.add(subInitialOS);
582            }
583    
584            ArrayList<ByteString> subAnyElements = getSubAnyElements();
585            if ((subAnyElements != null) && (! subAnyElements.isEmpty()))
586            {
587              for (ByteString s : subAnyElements)
588              {
589                ASN1OctetString os = s.toASN1OctetString();
590                os.setType(TYPE_SUBANY);
591                subElements.add(os);
592              }
593            }
594    
595            ByteString subFinalElement = getSubFinalElement();
596            if (subFinalElement != null)
597            {
598              ASN1OctetString subFinalOS =
599                   subFinalElement.toASN1OctetString();
600              subFinalOS.setType(TYPE_SUBFINAL);
601              subElements.add(subFinalOS);
602            }
603    
604            elements.add(new ASN1Sequence(subElements));
605            return new ASN1Sequence(filterType.getBERType(), elements);
606          case PRESENT:
607            return new ASN1OctetString(filterType.getBERType(),
608                                       getAttributeType());
609          case EXTENSIBLE_MATCH:
610            elements = new ArrayList<ASN1Element>(4);
611    
612            String matchingRuleID = getMatchingRuleID();
613            if (matchingRuleID != null)
614            {
615              elements.add(new ASN1OctetString(TYPE_MATCHING_RULE_ID,
616                                               matchingRuleID));
617            }
618    
619            attributeType = getAttributeType();
620            if (attributeType != null)
621            {
622              elements.add(new ASN1OctetString(TYPE_MATCHING_RULE_TYPE,
623                                               attributeType));
624            }
625    
626            ASN1OctetString assertionValueOS =
627                 getAssertionValue().toASN1OctetString();
628            assertionValueOS.setType(TYPE_MATCHING_RULE_VALUE);
629            elements.add(assertionValueOS);
630    
631            if (getDNAttributes())
632            {
633              elements.add(new ASN1Boolean(
634                   TYPE_MATCHING_RULE_DN_ATTRIBUTES, true));
635            }
636    
637            return new ASN1Sequence(filterType.getBERType(), elements);
638          default:
639            if (debugEnabled())
640            {
641              TRACER.debugError("Invalid search filter type: %s",
642                                filterType);
643            }
644            return null;
645        }
646      }
647    
648    
649    
650      /**
651       * Decodes the provided ASN.1 element as a raw search filter.
652       *
653       * @param  element  The ASN.1 element to decode.
654       *
655       * @return  The decoded search filter.
656       *
657       * @throws  LDAPException  If the provided ASN.1 element cannot be
658       *                         decoded as a raw search filter.
659       */
660      public static LDAPFilter decode(ASN1Element element)
661             throws LDAPException
662      {
663        if (element == null)
664        {
665          Message message = ERR_LDAP_FILTER_DECODE_NULL.get();
666          throw new LDAPException(PROTOCOL_ERROR, message);
667        }
668    
669        switch (element.getType())
670        {
671          case TYPE_FILTER_AND:
672          case TYPE_FILTER_OR:
673            return decodeCompoundFilter(element);
674    
675          case TYPE_FILTER_NOT:
676            return decodeNotFilter(element);
677    
678          case TYPE_FILTER_EQUALITY:
679          case TYPE_FILTER_GREATER_OR_EQUAL:
680          case TYPE_FILTER_LESS_OR_EQUAL:
681          case TYPE_FILTER_APPROXIMATE:
682            return decodeTypeAndValueFilter(element);
683    
684          case TYPE_FILTER_SUBSTRING:
685            return decodeSubstringFilter(element);
686    
687          case TYPE_FILTER_PRESENCE:
688            return decodePresenceFilter(element);
689    
690          case TYPE_FILTER_EXTENSIBLE_MATCH:
691            return decodeExtensibleMatchFilter(element);
692    
693          default:
694            Message message = ERR_LDAP_FILTER_DECODE_INVALID_TYPE.get(
695                element.getType());
696            throw new LDAPException(PROTOCOL_ERROR, message);
697        }
698      }
699    
700    
701    
702      /**
703       * Decodes the provided ASN.1 element as a compound filter (i.e.,
704       * one that holds a set of subordinate filter components, like AND
705       * or OR filters).
706       *
707       * @param  element  the ASN.1 element to decode.
708       *
709       * @return  The decoded LDAP search filter.
710       *
711       * @throws  LDAPException  If a problem occurs while trying to
712       *                         decode the provided ASN.1 element as a
713       *                         raw search filter.
714       */
715      private static LDAPFilter decodeCompoundFilter(ASN1Element element)
716              throws LDAPException
717      {
718        FilterType filterType;
719        switch (element.getType())
720        {
721          case TYPE_FILTER_AND:
722            filterType = FilterType.AND;
723            break;
724          case TYPE_FILTER_OR:
725            filterType = FilterType.OR;
726            break;
727          default:
728            // This should never happen.
729            if (debugEnabled())
730            {
731              TRACER.debugError("Invalid filter type %x for a " +
732                  "compound filter", element.getType());
733            }
734            filterType = null;
735        }
736    
737    
738        ArrayList<ASN1Element> elements;
739        try
740        {
741          elements = element.decodeAsSet().elements();
742        }
743        catch (Exception e)
744        {
745          if (debugEnabled())
746          {
747            TRACER.debugCaught(DebugLogLevel.ERROR, e);
748          }
749    
750          Message message =
751              ERR_LDAP_FILTER_DECODE_COMPOUND_SET.get(String.valueOf(e));
752          throw new LDAPException(PROTOCOL_ERROR, message, e);
753        }
754    
755    
756        ArrayList<RawFilter> filterComponents =
757             new ArrayList<RawFilter>(elements.size());
758        try
759        {
760          for (ASN1Element e : elements)
761          {
762            filterComponents.add(LDAPFilter.decode(e));
763          }
764        }
765        catch (LDAPException le)
766        {
767          throw le;
768        }
769        catch (Exception e)
770        {
771          if (debugEnabled())
772          {
773            TRACER.debugCaught(DebugLogLevel.ERROR, e);
774          }
775    
776          Message message = ERR_LDAP_FILTER_DECODE_COMPOUND_COMPONENTS.
777              get(String.valueOf(e));
778          throw new LDAPException(PROTOCOL_ERROR, message, e);
779        }
780    
781    
782        return new LDAPFilter(filterType, filterComponents, null, null,
783                              null, null, null, null, null, false);
784      }
785    
786    
787    
788      /**
789       * Decodes the provided ASN.1 element as a NOT filter.
790       *
791       * @param  element  the ASN.1 element to decode.
792       *
793       * @return  The decoded LDAP search filter.
794       *
795       * @throws  LDAPException  If a problem occurs while trying to
796       *                         decode the provided ASN.1 element as a
797       *                         raw search filter.
798       */
799      private static LDAPFilter decodeNotFilter(ASN1Element element)
800              throws LDAPException
801      {
802        ASN1Element notFilterElement;
803        try
804        {
805          notFilterElement = ASN1Element.decode(element.value());
806        }
807        catch (Exception e)
808        {
809          if (debugEnabled())
810          {
811            TRACER.debugCaught(DebugLogLevel.ERROR, e);
812          }
813    
814          Message message =
815              ERR_LDAP_FILTER_DECODE_NOT_ELEMENT.get(String.valueOf(e));
816          throw new LDAPException(PROTOCOL_ERROR, message, e);
817        }
818    
819    
820        RawFilter notComponent;
821        try
822        {
823          notComponent = decode(notFilterElement);
824        }
825        catch (LDAPException le)
826        {
827          throw le;
828        }
829        catch (Exception e)
830        {
831          if (debugEnabled())
832          {
833            TRACER.debugCaught(DebugLogLevel.ERROR, e);
834          }
835    
836          Message message =
837              ERR_LDAP_FILTER_DECODE_NOT_COMPONENT.get(String.valueOf(e));
838          throw new LDAPException(PROTOCOL_ERROR, message, e);
839        }
840    
841    
842        return new LDAPFilter(FilterType.NOT, null, notComponent, null,
843                              null, null, null, null, null, false);
844      }
845    
846    
847    
848      /**
849       * Decodes the provided ASN.1 element as a filter containing an
850       * attribute type and an assertion value.  This includes equality,
851       * greater or equal, less or equal, and approximate search filters.
852       *
853       * @param  element  the ASN.1 element to decode.
854       *
855       * @return  The decoded LDAP search filter.
856       *
857       * @throws  LDAPException  If a problem occurs while trying to
858       *                         decode the provided ASN.1 element as a
859       *                         raw search filter.
860       */
861      private static LDAPFilter decodeTypeAndValueFilter(
862                                     ASN1Element element)
863              throws LDAPException
864      {
865        FilterType filterType;
866        switch (element.getType())
867        {
868          case TYPE_FILTER_EQUALITY:
869            filterType = FilterType.EQUALITY;
870            break;
871          case TYPE_FILTER_GREATER_OR_EQUAL:
872            filterType = FilterType.GREATER_OR_EQUAL;
873            break;
874          case TYPE_FILTER_LESS_OR_EQUAL:
875            filterType = FilterType.LESS_OR_EQUAL;
876            break;
877          case TYPE_FILTER_APPROXIMATE:
878            filterType = FilterType.APPROXIMATE_MATCH;
879            break;
880          default:
881            // This should never happen.
882            if (debugEnabled())
883            {
884              TRACER.debugError("Invalid filter type %x for a " +
885                  "type-and-value filter", element.getType());
886            }
887            filterType = null;
888        }
889    
890    
891        ArrayList<ASN1Element> elements;
892        try
893        {
894          elements = element.decodeAsSequence().elements();
895        }
896        catch (Exception e)
897        {
898          if (debugEnabled())
899          {
900            TRACER.debugCaught(DebugLogLevel.ERROR, e);
901          }
902    
903          Message message =
904              ERR_LDAP_FILTER_DECODE_TV_SEQUENCE.get(String.valueOf(e));
905          throw new LDAPException(PROTOCOL_ERROR, message, e);
906        }
907    
908    
909        if (elements.size() != 2)
910        {
911          Message message =
912              ERR_LDAP_FILTER_DECODE_TV_INVALID_ELEMENT_COUNT.
913                get(elements.size());
914          throw new LDAPException(PROTOCOL_ERROR, message);
915        }
916    
917    
918        String attributeType;
919        try
920        {
921          attributeType =
922               elements.get(0).decodeAsOctetString().stringValue();
923        }
924        catch (Exception e)
925        {
926          if (debugEnabled())
927          {
928            TRACER.debugCaught(DebugLogLevel.ERROR, e);
929          }
930    
931          Message message =
932              ERR_LDAP_FILTER_DECODE_TV_TYPE.get(String.valueOf(e));
933          throw new LDAPException(PROTOCOL_ERROR, message, e);
934        }
935    
936    
937        ByteString assertionValue;
938        try
939        {
940          assertionValue = elements.get(1).decodeAsOctetString();
941        }
942        catch (Exception e)
943        {
944          if (debugEnabled())
945          {
946            TRACER.debugCaught(DebugLogLevel.ERROR, e);
947          }
948    
949          Message message =
950              ERR_LDAP_FILTER_DECODE_TV_VALUE.get(String.valueOf(e));
951          throw new LDAPException(PROTOCOL_ERROR, message, e);
952        }
953    
954    
955        return new LDAPFilter(filterType, null, null, attributeType,
956                              assertionValue, null, null, null, null,
957                              false);
958      }
959    
960    
961    
962      /**
963       * Decodes the provided ASN.1 element as a substring filter.
964       *
965       * @param  element  the ASN.1 element to decode.
966       *
967       * @return  The decoded LDAP search filter.
968       *
969       * @throws  LDAPException  If a problem occurs while trying to
970       *                         decode the provided ASN.1 element as a
971       *                         raw search filter.
972       */
973      private static LDAPFilter decodeSubstringFilter(ASN1Element element)
974              throws LDAPException
975      {
976        ArrayList<ASN1Element> elements;
977        try
978        {
979          elements = element.decodeAsSequence().elements();
980        }
981        catch (Exception e)
982        {
983          if (debugEnabled())
984          {
985            TRACER.debugCaught(DebugLogLevel.ERROR, e);
986          }
987    
988          Message message = ERR_LDAP_FILTER_DECODE_SUBSTRING_SEQUENCE.get(
989              String.valueOf(e));
990          throw new LDAPException(PROTOCOL_ERROR, message, e);
991        }
992    
993    
994        if (elements.size() != 2)
995        {
996          Message message =
997              ERR_LDAP_FILTER_DECODE_SUBSTRING_INVALID_ELEMENT_COUNT.
998                get(elements.size());
999          throw new LDAPException(PROTOCOL_ERROR, message);
1000        }
1001    
1002    
1003        String attributeType;
1004        try
1005        {
1006          attributeType =
1007               elements.get(0).decodeAsOctetString().stringValue();
1008        }
1009        catch (Exception e)
1010        {
1011          if (debugEnabled())
1012          {
1013            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1014          }
1015    
1016          Message message = ERR_LDAP_FILTER_DECODE_SUBSTRING_TYPE.get(
1017              String.valueOf(e));
1018          throw new LDAPException(PROTOCOL_ERROR, message, e);
1019        }
1020    
1021    
1022        ArrayList<ASN1Element> subElements;
1023        try
1024        {
1025          subElements = elements.get(1).decodeAsSequence().elements();
1026        }
1027        catch (Exception e)
1028        {
1029          if (debugEnabled())
1030          {
1031            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1032          }
1033    
1034          Message message = ERR_LDAP_FILTER_DECODE_SUBSTRING_ELEMENTS.get(
1035              String.valueOf(e));
1036          throw new LDAPException(PROTOCOL_ERROR, message, e);
1037        }
1038    
1039    
1040        if (subElements.isEmpty())
1041        {
1042          Message message =
1043              ERR_LDAP_FILTER_DECODE_SUBSTRING_NO_SUBELEMENTS.get();
1044          throw new LDAPException(PROTOCOL_ERROR, message);
1045        }
1046    
1047    
1048        ByteString subInitialElement = null;
1049        ByteString subFinalElement   = null;
1050        ArrayList<ByteString> subAnyElements = null;
1051        try
1052        {
1053          for (ASN1Element e : subElements)
1054          {
1055            switch (e.getType())
1056            {
1057              case TYPE_SUBINITIAL:
1058                subInitialElement = e.decodeAsOctetString();
1059                break;
1060              case TYPE_SUBFINAL:
1061                subFinalElement = e.decodeAsOctetString();
1062                break;
1063              case TYPE_SUBANY:
1064                if (subAnyElements == null)
1065                {
1066                  subAnyElements = new ArrayList<ByteString>();
1067                }
1068    
1069                subAnyElements.add(e.decodeAsOctetString());
1070                break;
1071              default:
1072                Message message =
1073                    ERR_LDAP_FILTER_DECODE_SUBSTRING_INVALID_SUBTYPE.
1074                      get(e.getType());
1075                throw new LDAPException(PROTOCOL_ERROR, message);
1076            }
1077          }
1078        }
1079        catch (LDAPException le)
1080        {
1081          throw le;
1082        }
1083        catch (Exception e)
1084        {
1085          if (debugEnabled())
1086          {
1087            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1088          }
1089    
1090          Message message = ERR_LDAP_FILTER_DECODE_SUBSTRING_VALUES.get(
1091              String.valueOf(e));
1092          throw new LDAPException(PROTOCOL_ERROR, message, e);
1093        }
1094    
1095    
1096        return new LDAPFilter(FilterType.SUBSTRING, null, null,
1097                              attributeType, null, subInitialElement,
1098                              subAnyElements, subFinalElement, null,
1099                              false);
1100      }
1101    
1102    
1103    
1104      /**
1105       * Decodes the provided ASN.1 element as a presence filter.
1106       *
1107       * @param  element  the ASN.1 element to decode.
1108       *
1109       * @return  The decoded LDAP search filter.
1110       *
1111       * @throws  LDAPException  If a problem occurs while trying to
1112       *                         decode the provided ASN.1 element as a
1113       *                         raw search filter.
1114       */
1115      private static LDAPFilter decodePresenceFilter(ASN1Element element)
1116              throws LDAPException
1117      {
1118        String attributeType;
1119        try
1120        {
1121          attributeType = element.decodeAsOctetString().stringValue();
1122        }
1123        catch (Exception e)
1124        {
1125          if (debugEnabled())
1126          {
1127            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1128          }
1129    
1130          Message message =
1131              ERR_LDAP_FILTER_DECODE_PRESENCE_TYPE.get(String.valueOf(e));
1132          throw new LDAPException(PROTOCOL_ERROR, message, e);
1133        }
1134    
1135    
1136        return new LDAPFilter(FilterType.PRESENT, null, null,
1137                              attributeType, null, null, null, null, null,
1138                              false);
1139      }
1140    
1141    
1142    
1143      /**
1144       * Decodes the provided ASN.1 element as an extensible match filter.
1145       *
1146       * @param  element  the ASN.1 element to decode.
1147       *
1148       * @return  The decoded LDAP search filter.
1149       *
1150       * @throws  LDAPException  If a problem occurs while trying to
1151       *                         decode the provided ASN.1 element as a
1152       *                         raw search filter.
1153       */
1154      private static LDAPFilter decodeExtensibleMatchFilter(ASN1Element
1155                                                                 element)
1156              throws LDAPException
1157      {
1158        ArrayList<ASN1Element> elements;
1159        try
1160        {
1161          elements = element.decodeAsSequence().elements();
1162        }
1163        catch (Exception e)
1164        {
1165          if (debugEnabled())
1166          {
1167            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1168          }
1169    
1170          Message message = ERR_LDAP_FILTER_DECODE_EXTENSIBLE_SEQUENCE.
1171              get(String.valueOf(e));
1172          throw new LDAPException(PROTOCOL_ERROR, message, e);
1173        }
1174    
1175    
1176        ByteString assertionValue = null;
1177        boolean    dnAttributes   = false;
1178        String     attributeType  = null;
1179        String     matchingRuleID = null;
1180        try
1181        {
1182          for (ASN1Element e : elements)
1183          {
1184            switch (e.getType())
1185            {
1186              case TYPE_MATCHING_RULE_ID:
1187                matchingRuleID = e.decodeAsOctetString().stringValue();
1188                break;
1189              case TYPE_MATCHING_RULE_TYPE:
1190                attributeType = e.decodeAsOctetString().stringValue();
1191                break;
1192              case TYPE_MATCHING_RULE_VALUE:
1193                assertionValue = e.decodeAsOctetString();
1194                break;
1195              case TYPE_MATCHING_RULE_DN_ATTRIBUTES:
1196                dnAttributes = e.decodeAsBoolean().booleanValue();
1197                break;
1198              default:
1199                Message message =
1200                    ERR_LDAP_FILTER_DECODE_EXTENSIBLE_INVALID_TYPE.
1201                      get(e.getType());
1202                throw new LDAPException(PROTOCOL_ERROR, message);
1203            }
1204          }
1205        }
1206        catch (LDAPException le)
1207        {
1208          throw le;
1209        }
1210        catch (Exception e)
1211        {
1212          if (debugEnabled())
1213          {
1214            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1215          }
1216    
1217          Message message = ERR_LDAP_FILTER_DECODE_EXTENSIBLE_ELEMENTS.
1218              get(String.valueOf(e));
1219          throw new LDAPException(PROTOCOL_ERROR, message, e);
1220        }
1221    
1222    
1223        return new LDAPFilter(FilterType.EXTENSIBLE_MATCH, null, null,
1224                              attributeType, assertionValue, null, null,
1225                              null, matchingRuleID, dnAttributes);
1226      }
1227    
1228    
1229    
1230      /**
1231       * Converts this raw filter to a search filter that may be used by
1232       * the Directory Server's core processing.
1233       *
1234       * @return  The generated search filter.
1235       *
1236       * @throws  DirectoryException  If a problem occurs while attempting
1237       *                              to construct the search filter.
1238       */
1239      public abstract SearchFilter toSearchFilter()
1240             throws DirectoryException;
1241    
1242    
1243    
1244      /**
1245       * Retrieves a string representation of this search filter.
1246       *
1247       * @return  A string representation of this search filter.
1248       */
1249      public String toString()
1250      {
1251        StringBuilder buffer = new StringBuilder();
1252        toString(buffer);
1253        return buffer.toString();
1254      }
1255    
1256    
1257    
1258      /**
1259       * Appends a string representation of this search filter to the
1260       * provided buffer.
1261       *
1262       * @param  buffer  The buffer to which the information should be
1263       *                 appended.
1264       */
1265      public abstract void toString(StringBuilder buffer);
1266    
1267    
1268    
1269      /**
1270       * Appends a properly-cleaned version of the provided value to the
1271       * given buffer so that it can be safely used in string
1272       * representations of this search filter.  The formatting changes
1273       * that may be performed will be in compliance with the
1274       * specification in RFC 2254.
1275       *
1276       * @param  buffer  The buffer to which the "safe" version of the
1277       *                 value will be appended.
1278       * @param  value   The value to be appended to the buffer.
1279       */
1280      public static void valueToFilterString(StringBuilder buffer,
1281                                             ByteString value)
1282      {
1283        if (value == null)
1284        {
1285          return;
1286        }
1287    
1288    
1289        // Get the binary representation of the value and iterate through
1290        // it to see if there are any unsafe characters.  If there are,
1291        // then escape them and replace them with a two-digit hex
1292        // equivalent.
1293        byte[] valueBytes = value.value();
1294        buffer.ensureCapacity(buffer.length() + valueBytes.length);
1295        for (byte b : valueBytes)
1296        {
1297          if (((b & 0x7F) != b) ||  // Not 7-bit clean
1298              (b <= 0x1F) ||        // Below the printable character range
1299              (b == 0x28) ||        // Open parenthesis
1300              (b == 0x29) ||        // Close parenthesis
1301              (b == 0x2A) ||        // Asterisk
1302              (b == 0x5C) ||        // Backslash
1303              (b == 0x7F))          // Delete character
1304          {
1305            buffer.append("\\");
1306            buffer.append(byteToHex(b));
1307          }
1308          else
1309          {
1310            buffer.append((char) b);
1311          }
1312        }
1313      }
1314    }
1315