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