001/*
002 * Copyright 2015-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2019 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.jsonfilter;
022
023
024
025import java.util.ArrayList;
026import java.util.Collection;
027import java.util.Collections;
028import java.util.HashSet;
029import java.util.LinkedHashMap;
030import java.util.List;
031import java.util.Set;
032
033import com.unboundid.util.Mutable;
034import com.unboundid.util.StaticUtils;
035import com.unboundid.util.ThreadSafety;
036import com.unboundid.util.ThreadSafetyLevel;
037import com.unboundid.util.json.JSONArray;
038import com.unboundid.util.json.JSONException;
039import com.unboundid.util.json.JSONObject;
040import com.unboundid.util.json.JSONString;
041import com.unboundid.util.json.JSONValue;
042
043
044
045/**
046 * This class provides an implementation of a JSON object filter that can
047 * perform a logical AND across the result obtained from a number of filters.
048 * The AND filter will match an object only if all of the filters contained in
049 * it match that object.  An AND filter with an empty set of embedded filters
050 * will match any object.
051 * <BR>
052 * <BLOCKQUOTE>
053 *   <B>NOTE:</B>  This class, and other classes within the
054 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
055 *   supported for use against Ping Identity, UnboundID, and
056 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
057 *   for proprietary functionality or for external specifications that are not
058 *   considered stable or mature enough to be guaranteed to work in an
059 *   interoperable way with other types of LDAP servers.
060 * </BLOCKQUOTE>
061 * <BR>
062 * The fields that are required to be included in an "AND" filter are:
063 * <UL>
064 *   <LI>
065 *     {@code andFilters} -- An array of JSON objects, each of which is a valid
066 *     JSON object filter.  Each of these filters must match a JSON object in
067 *     order for the AND filter to match.  If this is an empty array, then the
068 *     filter will match any object.
069 *   </LI>
070 * </UL>
071 * <BR><BR>
072 * <H2>Examples</H2>
073 * The following is an example of an AND filter that will match any JSON object:
074 * <PRE>
075 *   { "filterType" : "and",
076 *     "andFilters" : [ ] }
077 * </PRE>
078 * The above filter can be created with the code:
079 * <PRE>
080 *   ANDJSONObjectFilter filter = new ANDJSONObjectFilter();
081 * </PRE>
082 * <BR><BR>
083 * The following is an example of an AND filter that will match any JSON object
084 * in which there is a top-level field named "firstName" with a String value of
085 * "John" and top-level field named "lastName" with a String value of "Doe":
086 * <PRE>
087 *   { "filterType" : "and",
088 *     "andFilters" : [
089 *       { "filterType" : "equals",
090 *          "field" : "firstName",
091 *          "value" : "John" },
092 *       { "filterType" : "equals",
093 *          "field" : "lastName",
094 *          "value" : "Doe" } ] }
095 * </PRE>
096 * The above filter can be created with the code:
097 * <PRE>
098 *   ANDJSONObjectFilter filter = new ANDJSONObjectFilter(
099 *        new EqualsJSONObjectFilter("firstName", "John"),
100 *        new EqualsJSONObjectFilter("firstName", "Doe"));
101 * </PRE>
102 */
103@Mutable()
104@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
105public final class ANDJSONObjectFilter
106       extends JSONObjectFilter
107{
108  /**
109   * The value that should be used for the filterType element of the JSON object
110   * that represents an "AND" filter.
111   */
112  public static final String FILTER_TYPE = "and";
113
114
115
116  /**
117   * The name of the JSON field that is used to specify the set of filters to
118   * include in this AND filter.
119   */
120  public static final String FIELD_AND_FILTERS = "andFilters";
121
122
123
124  /**
125   * The pre-allocated set of required field names.
126   */
127  private static final Set<String> REQUIRED_FIELD_NAMES =
128       Collections.unmodifiableSet(new HashSet<>(
129            Collections.singletonList(FIELD_AND_FILTERS)));
130
131
132
133  /**
134   * The pre-allocated set of optional field names.
135   */
136  private static final Set<String> OPTIONAL_FIELD_NAMES =
137       Collections.emptySet();
138
139
140
141  /**
142   * The serial version UID for this serializable class.
143   */
144  private static final long serialVersionUID = 6616759665873968672L;
145
146
147
148  // The set of embedded filters for this AND filter.
149  private volatile List<JSONObjectFilter> andFilters;
150
151
152
153  /**
154   * Creates a new instance of this filter type with the provided information.
155   *
156   * @param  andFilters  The set of filters that must each match a JSON object
157   *                     in order for this AND filter to match.  If this is
158   *                     {@code null} or empty, then this AND filter will match
159   *                     any JSON object.
160   */
161  public ANDJSONObjectFilter(final JSONObjectFilter... andFilters)
162  {
163    this(StaticUtils.toList(andFilters));
164  }
165
166
167
168  /**
169   * Creates a new instance of this filter type with the provided information.
170   *
171   * @param  andFilters  The set of filters that must each match a JSON object
172   *                     in order for this AND filter to match.  If this is
173   *                     {@code null} or empty, then this AND filter will match
174   *                     any JSON object.
175   */
176  public ANDJSONObjectFilter(final Collection<JSONObjectFilter> andFilters)
177  {
178    setANDFilters(andFilters);
179  }
180
181
182
183  /**
184   * Retrieves the set of filters that must each match a JSON object in order
185   * for this AND filter to match.
186   *
187   * @return  The set of filters that must each match a JSON object in order for
188   *          this AND filter to match, or an empty list if this AND filter
189   *          should match any JSON object.
190   */
191  public List<JSONObjectFilter> getANDFilters()
192  {
193    return andFilters;
194  }
195
196
197
198  /**
199   * Specifies the set of AND filters that must each match a JSON object in
200   * order for this AND filter to match.
201   *
202   * @param  andFilters  The set of filters that must each match a JSON object
203   *                     in order for this AND filter to match.  If this is
204   *                     {@code null} or empty, then this AND filter will match
205   *                     any JSON object.
206   */
207  public void setANDFilters(final JSONObjectFilter... andFilters)
208  {
209    setANDFilters(StaticUtils.toList(andFilters));
210  }
211
212
213
214  /**
215   * Specifies the set of AND filters that must each match a JSON object in
216   * order for this AND filter to match.
217   *
218   * @param  andFilters  The set of filters that must each match a JSON object
219   *                     in order for this AND filter to match.  If this is
220   *                     {@code null} or empty, then this AND filter will match
221   *                     any JSON object.
222   */
223  public void setANDFilters(final Collection<JSONObjectFilter> andFilters)
224  {
225    if ((andFilters == null) || andFilters.isEmpty())
226    {
227      this.andFilters = Collections.emptyList();
228    }
229    else
230    {
231      this.andFilters =
232           Collections.unmodifiableList(new ArrayList<>(andFilters));
233    }
234  }
235
236
237
238  /**
239   * {@inheritDoc}
240   */
241  @Override()
242  public String getFilterType()
243  {
244    return FILTER_TYPE;
245  }
246
247
248
249  /**
250   * {@inheritDoc}
251   */
252  @Override()
253  protected Set<String> getRequiredFieldNames()
254  {
255    return REQUIRED_FIELD_NAMES;
256  }
257
258
259
260  /**
261   * {@inheritDoc}
262   */
263  @Override()
264  protected Set<String> getOptionalFieldNames()
265  {
266    return OPTIONAL_FIELD_NAMES;
267  }
268
269
270
271  /**
272   * {@inheritDoc}
273   */
274  @Override()
275  public boolean matchesJSONObject(final JSONObject o)
276  {
277    for (final JSONObjectFilter f : andFilters)
278    {
279      if (! f.matchesJSONObject(o))
280      {
281        return false;
282      }
283    }
284
285    return true;
286  }
287
288
289
290  /**
291   * {@inheritDoc}
292   */
293  @Override()
294  public JSONObject toJSONObject()
295  {
296    final LinkedHashMap<String,JSONValue> fields =
297         new LinkedHashMap<>(StaticUtils.computeMapCapacity(2));
298
299    fields.put(FIELD_FILTER_TYPE, new JSONString(FILTER_TYPE));
300
301    final ArrayList<JSONValue> filterValues =
302         new ArrayList<>(andFilters.size());
303    for (final JSONObjectFilter f : andFilters)
304    {
305      filterValues.add(f.toJSONObject());
306    }
307    fields.put(FIELD_AND_FILTERS, new JSONArray(filterValues));
308
309    return new JSONObject(fields);
310  }
311
312
313
314  /**
315   * {@inheritDoc}
316   */
317  @Override()
318  protected ANDJSONObjectFilter decodeFilter(final JSONObject filterObject)
319            throws JSONException
320  {
321    return new ANDJSONObjectFilter(getFilters(filterObject, FIELD_AND_FILTERS));
322  }
323}