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