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