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 055 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 056 * for proprietary functionality or for external specifications that are not 057 * considered stable or mature enough to be guaranteed to work in an 058 * interoperable way with 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<>( 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 = new LinkedHashMap<>(2); 239 240 fields.put(FIELD_FILTER_TYPE, new JSONString(FILTER_TYPE)); 241 fields.put(FIELD_NEGATE_FILTER, negateFilter.toJSONObject()); 242 243 return new JSONObject(fields); 244 } 245 246 247 248 /** 249 * {@inheritDoc} 250 */ 251 @Override() 252 protected NegateJSONObjectFilter decodeFilter(final JSONObject filterObject) 253 throws JSONException 254 { 255 final JSONValue v = filterObject.getField(FIELD_NEGATE_FILTER); 256 if (v == null) 257 { 258 throw new JSONException(ERR_OBJECT_FILTER_MISSING_REQUIRED_FIELD.get( 259 String.valueOf(filterObject), FILTER_TYPE, FIELD_NEGATE_FILTER)); 260 } 261 262 if (! (v instanceof JSONObject)) 263 { 264 throw new JSONException(ERR_OBJECT_FILTER_VALUE_NOT_OBJECT.get( 265 String.valueOf(filterObject), FILTER_TYPE, FIELD_NEGATE_FILTER)); 266 } 267 268 try 269 { 270 return new NegateJSONObjectFilter( 271 JSONObjectFilter.decode((JSONObject) v)); 272 } 273 catch (final JSONException e) 274 { 275 Debug.debugException(e); 276 throw new JSONException( 277 ERR_OBJECT_FILTER_VALUE_NOT_FILTER.get(String.valueOf(filterObject), 278 FILTER_TYPE, FIELD_NEGATE_FILTER, e.getMessage()), 279 e); 280 } 281 } 282}