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