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