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