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