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.Collections;
026    import java.util.HashSet;
027    import java.util.LinkedHashMap;
028    import java.util.Set;
029    
030    import com.unboundid.util.Debug;
031    import com.unboundid.util.Mutable;
032    import com.unboundid.util.ThreadSafety;
033    import com.unboundid.util.ThreadSafetyLevel;
034    import com.unboundid.util.Validator;
035    import com.unboundid.util.json.JSONException;
036    import com.unboundid.util.json.JSONObject;
037    import com.unboundid.util.json.JSONString;
038    import com.unboundid.util.json.JSONValue;
039    
040    import static com.unboundid.ldap.sdk.unboundidds.jsonfilter.JFMessages.*;
041    
042    
043    
044    /**
045     * <BLOCKQUOTE>
046     *   <B>NOTE:</B>  This class is part of the Commercial Edition of the UnboundID
047     *   LDAP SDK for Java.  It is not available for use in applications that
048     *   include only the Standard Edition of the LDAP SDK, and is not supported for
049     *   use in conjunction with non-UnboundID products.
050     * </BLOCKQUOTE>
051     * This class provides an implementation of a JSON object filter that can
052     * negate the result of a provided filter.  If the embedded filter matches a
053     * given JSON object, then this negate filter will not match that object.  If
054     * the embedded filter does not match a JSON object, then this negate filter
055     * will match that object.
056     * <BR><BR>
057     * The fields that are required to be included in a "negate" filter are:
058     * <UL>
059     *   <LI>
060     *     {@code negateFilter} -- The JSON object filter whose match result should
061     *     be negated.
062     *   </LI>
063     * </UL>
064     * <H2>Example</H2>
065     * The following is an example of a "negate" filter that will match any JSON
066     * object that does not have a top-level field named "userType" with a value of
067     * "employee":
068     * <PRE>
069     *   { "filterType" : "negate",
070     *     "negateFilter" : {
071     *       "filterType" : "equals",
072     *       "field" : "userType",
073     *       "value" : "employee" } }
074     * </PRE>
075     * The above filter can be created with the code:
076     * <PRE>
077     *   NegateJSONObjectFilter filter = new NegateJSONObjectFilter(
078     *        new EqualsJSONObjectFilter("userType", "employee"));
079     * </PRE>
080     */
081    @Mutable()
082    @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
083    public final class NegateJSONObjectFilter
084           extends JSONObjectFilter
085    {
086      /**
087       * The value that should be used for the filterType element of the JSON object
088       * that represents a "negate" filter.
089       */
090      public static final String FILTER_TYPE = "negate";
091    
092    
093    
094      /**
095       * The name of the JSON field that is used to specify the filter to negate.
096       */
097      public static final String FIELD_NEGATE_FILTER = "negateFilter";
098    
099    
100    
101      /**
102       * The pre-allocated set of required field names.
103       */
104      private static final Set<String> REQUIRED_FIELD_NAMES =
105           Collections.unmodifiableSet(new HashSet<String>(
106                Collections.singletonList(FIELD_NEGATE_FILTER)));
107    
108    
109    
110      /**
111       * The pre-allocated set of optional field names.
112       */
113      private static final Set<String> OPTIONAL_FIELD_NAMES =
114           Collections.emptySet();
115    
116    
117    
118      /**
119       * The serial version UID for this serializable class.
120       */
121      private static final long serialVersionUID = -9067967834329526711L;
122    
123    
124    
125      // The embedded filter whose result will be negated.
126      private volatile JSONObjectFilter negateFilter;
127    
128    
129    
130      /**
131       * Creates an instance of this filter type that can only be used for decoding
132       * JSON objects as "negate" filters.  It cannot be used as a regular "negate"
133       * filter.
134       */
135      NegateJSONObjectFilter()
136      {
137        negateFilter = null;
138      }
139    
140    
141    
142      /**
143       * Creates a new instance of this filter type with the provided information.
144       *
145       * @param  negateFilter  The JSON object filter whose match result should be
146       *                       negated.  It must not be {@code null}.
147       */
148      public NegateJSONObjectFilter(final JSONObjectFilter negateFilter)
149      {
150        Validator.ensureNotNull(negateFilter);
151    
152        this.negateFilter = negateFilter;
153      }
154    
155    
156    
157      /**
158       * Retrieves the JSON object filter whose match result will be negated.
159       *
160       * @return  The JSON object filter whose match result will be negated.
161       */
162      public JSONObjectFilter getNegateFilter()
163      {
164        return negateFilter;
165      }
166    
167    
168    
169      /**
170       * Specifies the JSON object filter whose match result should be negated.
171       *
172       * @param  negateFilter  The JSON object filter whose match result should be
173       *                       negated.
174       */
175      public void setNegateFilter(final JSONObjectFilter negateFilter)
176      {
177        Validator.ensureNotNull(negateFilter);
178    
179        this.negateFilter = negateFilter;
180      }
181    
182    
183    
184      /**
185       * {@inheritDoc}
186       */
187      @Override()
188      public String getFilterType()
189      {
190        return FILTER_TYPE;
191      }
192    
193    
194    
195      /**
196       * {@inheritDoc}
197       */
198      @Override()
199      protected Set<String> getRequiredFieldNames()
200      {
201        return REQUIRED_FIELD_NAMES;
202      }
203    
204    
205    
206      /**
207       * {@inheritDoc}
208       */
209      @Override()
210      protected Set<String> getOptionalFieldNames()
211      {
212        return OPTIONAL_FIELD_NAMES;
213      }
214    
215    
216    
217      /**
218       * {@inheritDoc}
219       */
220      @Override()
221      public boolean matchesJSONObject(final JSONObject o)
222      {
223        return (! negateFilter.matchesJSONObject(o));
224      }
225    
226    
227    
228      /**
229       * {@inheritDoc}
230       */
231      @Override()
232      public JSONObject toJSONObject()
233      {
234        final LinkedHashMap<String,JSONValue> fields =
235             new LinkedHashMap<String,JSONValue>(2);
236    
237        fields.put(FIELD_FILTER_TYPE, new JSONString(FILTER_TYPE));
238        fields.put(FIELD_NEGATE_FILTER, negateFilter.toJSONObject());
239    
240        return new JSONObject(fields);
241      }
242    
243    
244    
245      /**
246       * {@inheritDoc}
247       */
248      @Override()
249      protected NegateJSONObjectFilter decodeFilter(final JSONObject filterObject)
250                throws JSONException
251      {
252        final JSONValue v = filterObject.getField(FIELD_NEGATE_FILTER);
253        if (v == null)
254        {
255          throw new JSONException(ERR_OBJECT_FILTER_MISSING_REQUIRED_FIELD.get(
256               String.valueOf(filterObject), FILTER_TYPE, FIELD_NEGATE_FILTER));
257        }
258    
259        if (! (v instanceof JSONObject))
260        {
261          throw new JSONException(ERR_OBJECT_FILTER_VALUE_NOT_OBJECT.get(
262               String.valueOf(filterObject), FILTER_TYPE, FIELD_NEGATE_FILTER));
263        }
264    
265        try
266        {
267          return new NegateJSONObjectFilter(
268               JSONObjectFilter.decode((JSONObject) v));
269        }
270        catch (final JSONException e)
271        {
272          Debug.debugException(e);
273          throw new JSONException(
274               ERR_OBJECT_FILTER_VALUE_NOT_FILTER.get(String.valueOf(filterObject),
275                    FILTER_TYPE, FIELD_NEGATE_FILTER, e.getMessage()),
276               e);
277        }
278      }
279    }