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