001/* 002 * Copyright 2015-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2015-2024 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-2024 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.ArrayList; 041import java.util.Collection; 042import java.util.Collections; 043import java.util.HashSet; 044import java.util.LinkedHashMap; 045import java.util.List; 046import java.util.Set; 047 048import com.unboundid.util.Mutable; 049import com.unboundid.util.NotNull; 050import com.unboundid.util.Nullable; 051import com.unboundid.util.StaticUtils; 052import com.unboundid.util.ThreadSafety; 053import com.unboundid.util.ThreadSafetyLevel; 054import com.unboundid.util.json.JSONArray; 055import com.unboundid.util.json.JSONException; 056import com.unboundid.util.json.JSONObject; 057import com.unboundid.util.json.JSONString; 058import com.unboundid.util.json.JSONValue; 059 060 061 062/** 063 * This class provides an implementation of a JSON object filter that can 064 * perform a logical AND across the result obtained from a number of filters. 065 * The AND filter will match an object only if all of the filters contained in 066 * it match that object. An AND filter with an empty set of embedded filters 067 * will match any object. 068 * <BR> 069 * <BLOCKQUOTE> 070 * <B>NOTE:</B> This class, and other classes within the 071 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 072 * supported for use against Ping Identity, UnboundID, and 073 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 074 * for proprietary functionality or for external specifications that are not 075 * considered stable or mature enough to be guaranteed to work in an 076 * interoperable way with other types of LDAP servers. 077 * </BLOCKQUOTE> 078 * <BR> 079 * The fields that are required to be included in an "AND" filter are: 080 * <UL> 081 * <LI> 082 * {@code andFilters} -- An array of JSON objects, each of which is a valid 083 * JSON object filter. Each of these filters must match a JSON object in 084 * order for the AND filter to match. If this is an empty array, then the 085 * filter will match any object. 086 * </LI> 087 * </UL> 088 * <BR><BR> 089 * <H2>Examples</H2> 090 * The following is an example of an AND filter that will match any JSON object: 091 * <PRE> 092 * { "filterType" : "and", 093 * "andFilters" : [ ] } 094 * </PRE> 095 * The above filter can be created with the code: 096 * <PRE> 097 * ANDJSONObjectFilter filter = new ANDJSONObjectFilter(); 098 * </PRE> 099 * <BR><BR> 100 * The following is an example of an AND filter that will match any JSON object 101 * in which there is a top-level field named "firstName" with a String value of 102 * "John" and top-level field named "lastName" with a String value of "Doe": 103 * <PRE> 104 * { "filterType" : "and", 105 * "andFilters" : [ 106 * { "filterType" : "equals", 107 * "field" : "firstName", 108 * "value" : "John" }, 109 * { "filterType" : "equals", 110 * "field" : "lastName", 111 * "value" : "Doe" } ] } 112 * </PRE> 113 * The above filter can be created with the code: 114 * <PRE> 115 * ANDJSONObjectFilter filter = new ANDJSONObjectFilter( 116 * new EqualsJSONObjectFilter("firstName", "John"), 117 * new EqualsJSONObjectFilter("firstName", "Doe")); 118 * </PRE> 119 */ 120@Mutable() 121@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 122public final class ANDJSONObjectFilter 123 extends JSONObjectFilter 124{ 125 /** 126 * The value that should be used for the filterType element of the JSON object 127 * that represents an "AND" filter. 128 */ 129 @NotNull public static final String FILTER_TYPE = "and"; 130 131 132 133 /** 134 * The name of the JSON field that is used to specify the set of filters to 135 * include in this AND filter. 136 */ 137 @NotNull public static final String FIELD_AND_FILTERS = "andFilters"; 138 139 140 141 /** 142 * The pre-allocated set of required field names. 143 */ 144 @NotNull private static final Set<String> REQUIRED_FIELD_NAMES = 145 Collections.unmodifiableSet(new HashSet<>( 146 Collections.singletonList(FIELD_AND_FILTERS))); 147 148 149 150 /** 151 * The pre-allocated set of optional field names. 152 */ 153 @NotNull private static final Set<String> OPTIONAL_FIELD_NAMES = 154 Collections.emptySet(); 155 156 157 158 /** 159 * The serial version UID for this serializable class. 160 */ 161 private static final long serialVersionUID = 6616759665873968672L; 162 163 164 165 // The set of embedded filters for this AND filter. 166 @NotNull private volatile List<JSONObjectFilter> andFilters; 167 168 169 170 /** 171 * Creates a new instance of this filter type with the provided information. 172 * 173 * @param andFilters The set of filters that must each match a JSON object 174 * in order for this AND filter to match. If this is 175 * {@code null} or empty, then this AND filter will match 176 * any JSON object. 177 */ 178 public ANDJSONObjectFilter(@Nullable final JSONObjectFilter... andFilters) 179 { 180 this(StaticUtils.toList(andFilters)); 181 } 182 183 184 185 /** 186 * Creates a new instance of this filter type with the provided information. 187 * 188 * @param andFilters The set of filters that must each match a JSON object 189 * in order for this AND filter to match. If this is 190 * {@code null} or empty, then this AND filter will match 191 * any JSON object. 192 */ 193 public ANDJSONObjectFilter( 194 @Nullable final Collection<JSONObjectFilter> andFilters) 195 { 196 setANDFilters(andFilters); 197 } 198 199 200 201 /** 202 * Retrieves the set of filters that must each match a JSON object in order 203 * for this AND filter to match. 204 * 205 * @return The set of filters that must each match a JSON object in order for 206 * this AND filter to match, or an empty list if this AND filter 207 * should match any JSON object. 208 */ 209 @NotNull() 210 public List<JSONObjectFilter> getANDFilters() 211 { 212 return andFilters; 213 } 214 215 216 217 /** 218 * Specifies the set of AND filters that must each match a JSON object in 219 * order for this AND filter to match. 220 * 221 * @param andFilters The set of filters that must each match a JSON object 222 * in order for this AND filter to match. If this is 223 * {@code null} or empty, then this AND filter will match 224 * any JSON object. 225 */ 226 public void setANDFilters(@Nullable final JSONObjectFilter... andFilters) 227 { 228 setANDFilters(StaticUtils.toList(andFilters)); 229 } 230 231 232 233 /** 234 * Specifies the set of AND filters that must each match a JSON object in 235 * order for this AND filter to match. 236 * 237 * @param andFilters The set of filters that must each match a JSON object 238 * in order for this AND filter to match. If this is 239 * {@code null} or empty, then this AND filter will match 240 * any JSON object. 241 */ 242 public void setANDFilters( 243 @Nullable final Collection<JSONObjectFilter> andFilters) 244 { 245 if ((andFilters == null) || andFilters.isEmpty()) 246 { 247 this.andFilters = Collections.emptyList(); 248 } 249 else 250 { 251 this.andFilters = 252 Collections.unmodifiableList(new ArrayList<>(andFilters)); 253 } 254 } 255 256 257 258 /** 259 * {@inheritDoc} 260 */ 261 @Override() 262 @NotNull() 263 public String getFilterType() 264 { 265 return FILTER_TYPE; 266 } 267 268 269 270 /** 271 * {@inheritDoc} 272 */ 273 @Override() 274 @NotNull() 275 protected Set<String> getRequiredFieldNames() 276 { 277 return REQUIRED_FIELD_NAMES; 278 } 279 280 281 282 /** 283 * {@inheritDoc} 284 */ 285 @Override() 286 @NotNull() 287 protected Set<String> getOptionalFieldNames() 288 { 289 return OPTIONAL_FIELD_NAMES; 290 } 291 292 293 294 /** 295 * {@inheritDoc} 296 */ 297 @Override() 298 public boolean matchesJSONObject(@NotNull final JSONObject o) 299 { 300 for (final JSONObjectFilter f : andFilters) 301 { 302 if (! f.matchesJSONObject(o)) 303 { 304 return false; 305 } 306 } 307 308 return true; 309 } 310 311 312 313 /** 314 * {@inheritDoc} 315 */ 316 @Override() 317 @NotNull() 318 public JSONObject toJSONObject() 319 { 320 final LinkedHashMap<String,JSONValue> fields = 321 new LinkedHashMap<>(StaticUtils.computeMapCapacity(2)); 322 323 fields.put(FIELD_FILTER_TYPE, new JSONString(FILTER_TYPE)); 324 325 final ArrayList<JSONValue> filterValues = 326 new ArrayList<>(andFilters.size()); 327 for (final JSONObjectFilter f : andFilters) 328 { 329 filterValues.add(f.toJSONObject()); 330 } 331 fields.put(FIELD_AND_FILTERS, new JSONArray(filterValues)); 332 333 return new JSONObject(fields); 334 } 335 336 337 338 /** 339 * {@inheritDoc} 340 */ 341 @Override() 342 @NotNull() 343 public JSONObject toNormalizedJSONObject() 344 { 345 final LinkedHashMap<String,JSONValue> fields = 346 new LinkedHashMap<>(StaticUtils.computeMapCapacity(2)); 347 348 fields.put(FIELD_FILTER_TYPE, new JSONString(FILTER_TYPE)); 349 350 final ArrayList<JSONValue> filterValues = 351 new ArrayList<>(andFilters.size()); 352 for (final JSONObjectFilter f : andFilters) 353 { 354 filterValues.add(f.toNormalizedJSONObject()); 355 } 356 fields.put(FIELD_AND_FILTERS, new JSONArray(filterValues)); 357 358 return new JSONObject(fields); 359 } 360 361 362 363 /** 364 * {@inheritDoc} 365 */ 366 @Override() 367 @NotNull() 368 protected ANDJSONObjectFilter decodeFilter( 369 @NotNull final JSONObject filterObject) 370 throws JSONException 371 { 372 return new ANDJSONObjectFilter(getFilters(filterObject, FIELD_AND_FILTERS)); 373 } 374}