001    /*
002     * Copyright 2009-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2015 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.unboundidds.examples;
022    
023    
024    
025    import java.io.Serializable;
026    import java.util.Arrays;
027    import java.util.TreeSet;
028    
029    import com.unboundid.ldap.sdk.Filter;
030    import com.unboundid.util.NotMutable;
031    import com.unboundid.util.ThreadSafety;
032    import com.unboundid.util.ThreadSafetyLevel;
033    
034    import static com.unboundid.util.StaticUtils.*;
035    
036    
037    
038    /**
039     * <BLOCKQUOTE>
040     *   <B>NOTE:</B>  This class is part of the Commercial Edition of the UnboundID
041     *   LDAP SDK for Java.  It is not available for use in applications that
042     *   include only the Standard Edition of the LDAP SDK, and is not supported for
043     *   use in conjunction with non-UnboundID products.
044     * </BLOCKQUOTE>
045     * This class provides a data structure for representing search filters in a
046     * generic way.  This includes:
047     * <UL>
048     *   <LI>Using a consistent order for AND and OR components.</LI>
049     *   <LI>Converting all attribute names to lowercase.</LI>
050     *   <LI>Replacing the assertion value with a "?" character for equality,
051     *       greater-or-equal, less-or-equal, approximate match, and extensible
052     *       match filters.</LI>
053     *   <LI>Replacing all subInitial, subAny, and subFinal elements with "?"
054     *       characters in substring filters.</LI>
055     * </UL>
056     */
057    @NotMutable()
058    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
059    public final class GenericFilter
060           implements Serializable
061    {
062      /**
063       * The serial version UID for this serializable class.
064       */
065      private static final long serialVersionUID = -7875317078624475546L;
066    
067    
068    
069      // The hash code for this generic filter.
070      private final int hashCode;
071    
072      // The string representation for this filter.
073      private final String filterString;
074    
075    
076    
077      /**
078       * Creates a new generic filter from the provided search filter.
079       *
080       * @param  f  The filter to use to create a generic filte.r
081       */
082      public GenericFilter(final Filter f)
083      {
084        final StringBuilder b = new StringBuilder();
085        b.append('(');
086    
087        switch (f.getFilterType())
088        {
089          case Filter.FILTER_TYPE_AND:
090          case Filter.FILTER_TYPE_OR:
091            appendComponents(f, b);
092            break;
093    
094          case Filter.FILTER_TYPE_NOT:
095            b.append('!');
096            b.append(new GenericFilter(f.getNOTComponent()).toString());
097            break;
098    
099          case Filter.FILTER_TYPE_EQUALITY:
100            b.append(toLowerCase(f.getAttributeName()));
101            b.append("=?");
102            break;
103    
104          case Filter.FILTER_TYPE_SUBSTRING:
105            b.append(toLowerCase(f.getAttributeName()));
106            b.append('=');
107            if (f.getRawSubInitialValue() != null)
108            {
109              b.append('?');
110            }
111            for (int i=0; i < f.getRawSubAnyValues().length; i++)
112            {
113              b.append("*?");
114            }
115            b.append('*');
116            if (f.getRawSubFinalValue() != null)
117            {
118              b.append('?');
119            }
120            break;
121    
122          case Filter.FILTER_TYPE_GREATER_OR_EQUAL:
123            b.append(toLowerCase(f.getAttributeName()));
124            b.append(">=?");
125            break;
126    
127          case Filter.FILTER_TYPE_LESS_OR_EQUAL:
128            b.append(toLowerCase(f.getAttributeName()));
129            b.append("<=?");
130            break;
131    
132          case Filter.FILTER_TYPE_PRESENCE:
133            b.append(toLowerCase(f.getAttributeName()));
134            b.append("=*");
135            break;
136    
137          case Filter.FILTER_TYPE_APPROXIMATE_MATCH:
138            b.append(toLowerCase(f.getAttributeName()));
139            b.append("~=?");
140            break;
141    
142          case Filter.FILTER_TYPE_EXTENSIBLE_MATCH:
143            final String attrName = toLowerCase(f.getAttributeName());
144            final String mrID     = toLowerCase(f.getMatchingRuleID());
145            if (attrName != null)
146            {
147              b.append(attrName);
148            }
149            if (f.getDNAttributes())
150            {
151              b.append(":dn");
152            }
153            if (mrID != null)
154            {
155              b.append(':');
156              b.append(mrID);
157            }
158            b.append(":=?");
159            break;
160        }
161    
162        b.append(')');
163    
164        filterString = b.toString();
165        hashCode     = filterString.hashCode();
166      }
167    
168    
169    
170      /**
171       * Appends a string representation of the provided AND or OR filter to the
172       * given buffer.
173       *
174       * @param  f  The filter for which to provide the string representation.
175       * @param  b  The buffer to which to append the string representation.
176       */
177      private static void appendComponents(final Filter f, final StringBuilder b)
178      {
179        if (f.getFilterType() == Filter.FILTER_TYPE_AND)
180        {
181          b.append('&');
182        }
183        else
184        {
185          b.append('|');
186        }
187    
188        final TreeSet<Filter> compSet =
189             new TreeSet<Filter>(FilterComparator.getInstance());
190        compSet.addAll(Arrays.asList(f.getComponents()));
191        for (final Filter fc : compSet)
192        {
193          b.append(new GenericFilter(fc).toString());
194        }
195      }
196    
197    
198    
199      /**
200       * Retrieves a hash code for this generic filter.
201       *
202       * @return  A hash code for this generic filter.
203       */
204      @Override()
205      public int hashCode()
206      {
207        return hashCode;
208      }
209    
210    
211    
212      /**
213       * Indicates whether the provided object is equal to this generic filter.
214       *
215       * @param  o  The object for which to make the determination.
216       *
217       * @return  {@code true} the provided object is equal to this generic filter,
218       *          or {@code false} if not.
219       */
220      @Override()
221      public boolean equals(final Object o)
222      {
223        if (o == null)
224        {
225          return false;
226        }
227    
228        if (o == this)
229        {
230          return true;
231        }
232    
233        return ((o instanceof GenericFilter) &&
234                filterString.equals(((GenericFilter) o).filterString));
235      }
236    
237    
238    
239      /**
240       * Retrieves a string representation of this generic filter.
241       *
242       * @return  A string representation of this generic filter.
243       */
244      @Override()
245      public String toString()
246      {
247        return filterString;
248      }
249    }