001 /*
002 * Copyright 2011-2012 UnboundID Corp.
003 *
004 * This program is free software; you can redistribute it and/or modify
005 * it under the terms of the GNU General Public License (GPLv2 only)
006 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
007 * as published by the Free Software Foundation.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program; if not, see <http://www.gnu.org/licenses>.
016 */
017
018 package com.unboundid.scim.sdk;
019
020 import java.util.Collection;
021 import java.util.Collections;
022 import java.util.HashMap;
023 import java.util.LinkedHashMap;
024 import java.util.List;
025 import java.util.Map;
026 import java.util.Set;
027
028 import static com.unboundid.scim.sdk.StaticUtils.toLowerCase;
029
030
031
032 /**
033 * This class represents a Simple Cloud Identity Management (SCIM) object.
034 * A SCIM object may be composed of common schema attributes and a collection
035 * of attributes from one or more additional schema definitions.
036 * This class is not designed to be thread-safe.
037 */
038 public class SCIMObject
039 {
040
041 /**
042 * The set of attributes in this object grouped by the URI of the schema to
043 * which they belong.
044 */
045 private final HashMap<String,LinkedHashMap<String,SCIMAttribute>> attributes;
046
047
048
049 /**
050 * Create an empty SCIM object that initially has no attributes. The type of
051 * resource is not specified.
052 */
053 public SCIMObject()
054 {
055 this.attributes =
056 new HashMap<String, LinkedHashMap<String, SCIMAttribute>>();
057 }
058
059
060 /**
061 * Create a new copy of the provided SCIM object.
062 *
063 * @param scimObject The SCIMObject to copy.
064 */
065 public SCIMObject(final SCIMObject scimObject)
066 {
067 // Since SCIMAttribute is immutable, just copy the maps.
068 this.attributes =
069 new HashMap<String, LinkedHashMap<String, SCIMAttribute>>();
070 for(Map.Entry<String, LinkedHashMap<String, SCIMAttribute>> entry :
071 scimObject.attributes.entrySet())
072 {
073 this.attributes.put(entry.getKey(),
074 new LinkedHashMap<String, SCIMAttribute>(entry.getValue()));
075 }
076 }
077
078
079
080 /**
081 * Retrieves the set of schemas currently contributing attributes to this
082 * object.
083 *
084 * @return An immutable collection of the URIs of schemas currently
085 * contributing attributes to this object.
086 */
087 public Set<String> getSchemas()
088 {
089 return Collections.unmodifiableSet(attributes.keySet());
090 }
091
092
093
094 /**
095 * Determines whether this object contains any attributes in the specified
096 * schema.
097 *
098 * @param schema The URI of the schema for which to make the determination.
099 * It must not be {@code null}.
100 *
101 * @return {@code true} if this object contains any attributes in the
102 * specified schema, or {@code false} if not.
103 */
104 public boolean hasSchema(final String schema)
105 {
106 return attributes.containsKey(toLowerCase(schema));
107 }
108
109
110
111 /**
112 * Retrieves the attribute with the specified name.
113 *
114 * @param schema The URI of the schema containing the attribute to retrieve.
115 *
116 * @param name The name of the attribute to retrieve. It must not be
117 * {@code null}.
118 *
119 * @return The requested attribute from this object, or {@code null} if the
120 * specified attribute is not present in this object.
121 */
122 public SCIMAttribute getAttribute(final String schema, final String name)
123 {
124 final LinkedHashMap<String,SCIMAttribute> attrs =
125 attributes.get(toLowerCase(schema));
126
127 if (attrs == null)
128 {
129 return null;
130 }
131 else
132 {
133 return attrs.get(toLowerCase(name));
134 }
135 }
136
137
138
139 /**
140 * Retrieves the set of attributes in this object from the specified schema.
141 *
142 * @param schema The URI of the schema whose attributes are to be retrieved.
143 *
144 * @return An immutable collection of the attributes in this object from the
145 * specified schema, or the empty collection if there are no such
146 * attributes.
147 */
148 public Collection<SCIMAttribute> getAttributes(final String schema)
149 {
150 final LinkedHashMap<String, SCIMAttribute> attrs =
151 attributes.get(toLowerCase(schema));
152
153 if (attrs == null)
154 {
155 return Collections.emptyList();
156 }
157 else
158 {
159 return Collections.unmodifiableCollection(attrs.values());
160 }
161 }
162
163
164
165 /**
166 * Determines whether this object contains the specified attribute.
167 *
168 * @param schema The URI of the schema containing the attribute.
169 * @param name The name of the attribute for which to make the
170 * determination. It must not be {@code null}.
171 *
172 * @return {@code true} if this object contains the specified attribute, or
173 * {@code false} if not.
174 */
175 public boolean hasAttribute(final String schema, final String name)
176 {
177 final LinkedHashMap<String, SCIMAttribute> attrs =
178 attributes.get(toLowerCase(schema));
179
180 if (attrs == null)
181 {
182 return false;
183 }
184 else
185 {
186 return attrs.containsKey(toLowerCase(name));
187 }
188 }
189
190
191
192 /**
193 * Adds the provided attribute to this object. If this object already contains
194 * an attribute with the same name from the same schema, then the provided
195 * attribute will not be added.
196 *
197 * @param attribute The attribute to be added. It must not be {@code null}.
198 *
199 * @return {@code true} if the object was updated, or {@code false} if the
200 * object already contained an attribute with the same name.
201 */
202 public boolean addAttribute(final SCIMAttribute attribute)
203 {
204 final String lowerCaseSchema = toLowerCase(attribute.getSchema());
205 final String lowerCaseName = toLowerCase(attribute.getName());
206
207 LinkedHashMap<String,SCIMAttribute> attrs = attributes.get(lowerCaseSchema);
208 if (attrs == null)
209 {
210 attrs = new LinkedHashMap<String, SCIMAttribute>();
211 attrs.put(lowerCaseName, attribute);
212 attributes.put(lowerCaseSchema, attrs);
213 return true;
214 }
215 else
216 {
217 if (attrs.containsKey(lowerCaseName))
218 {
219 return false;
220 }
221 else
222 {
223 attrs.put(lowerCaseName, attribute);
224 return true;
225 }
226 }
227 }
228
229
230
231 /**
232 * Adds the provided attribute to this object, replacing any existing
233 * attribute with the same name.
234 *
235 * @param attribute The attribute to be added. It must not be {@code null}.
236 */
237 public void setAttribute(final SCIMAttribute attribute)
238 {
239 final String lowerCaseSchema = toLowerCase(attribute.getSchema());
240 final String lowerCaseName = toLowerCase(attribute.getName());
241
242 LinkedHashMap<String,SCIMAttribute> attrs = attributes.get(lowerCaseSchema);
243 if (attrs == null)
244 {
245 attrs = new LinkedHashMap<String, SCIMAttribute>();
246 attrs.put(lowerCaseName, attribute);
247 attributes.put(lowerCaseSchema, attrs);
248 }
249 else
250 {
251 attrs.put(lowerCaseName, attribute);
252 }
253 }
254
255
256
257 /**
258 * Removes the specified attribute from this object.
259 *
260 * @param schema The URI of the schema to which the attribute belongs.
261 * @param name The name of the attribute to remove. It must not be
262 * {@code null}.
263 *
264 * @return {@code true} if the attribute was removed from the object, or
265 * {@code false} if it was not present.
266 */
267 public boolean removeAttribute(final String schema, final String name)
268 {
269 final String lowerCaseSchema = toLowerCase(schema);
270 LinkedHashMap<String,SCIMAttribute> attrs = attributes.get(lowerCaseSchema);
271 if (attrs == null)
272 {
273 return false;
274 }
275 else
276 {
277 final boolean removed = attrs.remove(toLowerCase(name)) != null;
278 if (removed && attrs.isEmpty())
279 {
280 attributes.remove(lowerCaseSchema);
281 }
282 return removed;
283 }
284 }
285
286
287
288 /**
289 * Determine whether this object matches the provided filter parameters.
290 *
291 * @param filter The filter parameters to compare against the object.
292 *
293 * @return {@code true} if this object matches the provided filter, and
294 * {@code false} otherwise.
295 */
296 public boolean matchesFilter(final SCIMFilter filter)
297 {
298 final SCIMFilterType type = filter.getFilterType();
299 final List<SCIMFilter> components = filter.getFilterComponents();
300
301 switch(type)
302 {
303 case AND:
304 for(SCIMFilter component : components)
305 {
306 if(!matchesFilter(component))
307 {
308 return false;
309 }
310 }
311 return true;
312 case OR:
313 for(SCIMFilter component : components)
314 {
315 if(matchesFilter(component))
316 {
317 return true;
318 }
319 }
320 return false;
321 }
322
323 final String schema = filter.getFilterAttribute().getAttributeSchema();
324 final String attributeName = filter.getFilterAttribute().getAttributeName();
325
326 final SCIMAttribute attribute = getAttribute(schema, attributeName);
327 if (attribute == null)
328 {
329 return false;
330 }
331
332 return attribute.matchesFilter(filter);
333 }
334
335
336 /**
337 * {@inheritDoc}
338 */
339 @Override
340 public boolean equals(final Object o) {
341 if (this == o) {
342 return true;
343 }
344 if (o == null || getClass() != o.getClass()) {
345 return false;
346 }
347
348 SCIMObject that = (SCIMObject) o;
349
350 if (!attributes.equals(that.attributes)) {
351 return false;
352 }
353
354 return true;
355 }
356
357
358 /**
359 * {@inheritDoc}
360 */
361 @Override
362 public int hashCode() {
363 return attributes.hashCode();
364 }
365
366
367 /**
368 * {@inheritDoc}
369 */
370 @Override
371 public String toString() {
372 return "SCIMObject{" +
373 "attributes=" + attributes +
374 '}';
375 }
376 }