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