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 com.unboundid.scim.schema.AttributeDescriptor;
021 import com.unboundid.scim.schema.ResourceDescriptor;
022
023 import java.util.ArrayList;
024 import java.util.HashMap;
025 import java.util.HashSet;
026 import java.util.Map;
027 import java.util.Set;
028
029
030
031 /**
032 * This class represents a list of query attributes taken from the attributes
033 * query parameter. e.g. attributes=name.formatted,userName
034 */
035 public class SCIMQueryAttributes
036 {
037 /**
038 * Indicates whether all attributes and sub-attributes are requested.
039 */
040 private final boolean allAttributesRequested;
041
042 /**
043 * The set of attributes and sub-attributes explicitly requested.
044 */
045 private final Map<AttributeDescriptor,Set<AttributeDescriptor>> descriptors;
046
047
048
049 /**
050 * Create a new instance of query attributes from their string representation.
051 *
052 * @param resourceDescriptor The resource descriptor for the SCIM endpoint.
053 * @param attributes The attributes query parameter specifying the set of
054 * attributes or sub-attributes requested, or null if
055 * all attributes and sub-attributes are requested. The
056 * attributes must be qualified by their
057 * schema URI if they are not in the core schema.
058 *
059 * @throws InvalidResourceException If one of the specified attributes does
060 * not exist.
061 */
062 public SCIMQueryAttributes(final ResourceDescriptor resourceDescriptor,
063 final String attributes)
064 throws InvalidResourceException
065 {
066 descriptors =
067 new HashMap<AttributeDescriptor, Set<AttributeDescriptor>>();
068
069 if (attributes == null)
070 {
071 allAttributesRequested = true;
072 }
073 else
074 {
075 allAttributesRequested = false;
076 if (!attributes.isEmpty())
077 {
078 final String[] paths = attributes.split(",");
079 if (paths.length > 0)
080 {
081 for (final String a : paths)
082 {
083 final AttributePath path = AttributePath.parse(a);
084 final AttributeDescriptor attributeDescriptor =
085 resourceDescriptor.getAttribute(path.getAttributeSchema(),
086 path.getAttributeName());
087
088 Set<AttributeDescriptor> subAttributes =
089 descriptors.get(attributeDescriptor);
090 if (subAttributes == null)
091 {
092 subAttributes = new HashSet<AttributeDescriptor>();
093 if (path.getSubAttributeName() != null)
094 {
095 subAttributes.add(
096 attributeDescriptor.getSubAttribute(
097 path.getSubAttributeName()));
098 }
099 descriptors.put(attributeDescriptor, subAttributes);
100 }
101 else
102 {
103 if (!subAttributes.isEmpty())
104 {
105 if (path.getSubAttributeName() != null)
106 {
107 subAttributes.add(
108 attributeDescriptor.getSubAttribute(
109 path.getSubAttributeName()));
110 }
111 else
112 {
113 subAttributes.clear();
114 }
115 }
116 }
117 }
118 }
119 }
120
121 final AttributeDescriptor id =
122 resourceDescriptor.getAttribute(SCIMConstants.SCHEMA_URI_CORE, "id");
123 if (!descriptors.containsKey(id))
124 {
125 descriptors.put(id, new HashSet<AttributeDescriptor>());
126 }
127
128 final AttributeDescriptor meta =
129 resourceDescriptor.getAttribute(SCIMConstants.SCHEMA_URI_CORE,
130 "meta");
131 if (!descriptors.containsKey(meta))
132 {
133 descriptors.put(meta, new HashSet<AttributeDescriptor>());
134 }
135 }
136 }
137
138
139
140 /**
141 * Create a new set of query attributes from the provided information.
142 *
143 * @param descriptors The set of attributes and sub-attributes
144 * explicitly requested, or {@code null} if all
145 * attributes are requested.
146 */
147 private SCIMQueryAttributes(
148 final Map<AttributeDescriptor,Set<AttributeDescriptor>> descriptors)
149 {
150 this.allAttributesRequested = (descriptors == null);
151 this.descriptors = descriptors;
152 }
153
154
155
156 /**
157 * Determine whether all attributes and sub-attributes are requested by
158 * these query attributes.
159 *
160 * @return {@code true} if all attributes and sub-attributes are requested,
161 * and {@code false} otherwise.
162 */
163 public boolean allAttributesRequested()
164 {
165 return allAttributesRequested;
166 }
167
168
169
170 /**
171 * Determine whether the specified attribute is requested by these query
172 * attributes.
173 *
174 * @param attributeDescriptor The attribute for which to make the
175 * determination.
176 *
177 * @return {@code true} if the specified attribute is requested, or false
178 * otherwise.
179 */
180 public boolean isAttributeRequested(
181 final AttributeDescriptor attributeDescriptor)
182 {
183 return allAttributesRequested() ||
184 descriptors.containsKey(attributeDescriptor);
185 }
186
187
188
189 /**
190 * Pare down a SCIM object to its requested attributes.
191 *
192 * @param scimObject The SCIM object to be pared down.
193 *
194 * @return The pared down SCIM object.
195 */
196 public SCIMObject pareObject(final SCIMObject scimObject)
197 {
198 if (allAttributesRequested())
199 {
200 return scimObject;
201 }
202
203 final SCIMObject paredObject = new SCIMObject();
204 for (final Map.Entry<AttributeDescriptor,Set<AttributeDescriptor>> entry :
205 descriptors.entrySet())
206 {
207 final AttributeDescriptor attributeDescriptor = entry.getKey();
208
209 final SCIMAttribute a =
210 scimObject.getAttribute(attributeDescriptor.getSchema(),
211 attributeDescriptor.getName());
212 if (a != null)
213 {
214 final SCIMAttribute paredAttribute = pareAttribute(a);
215 if (paredAttribute != null)
216 {
217 paredObject.addAttribute(paredAttribute);
218 }
219 }
220 }
221
222 return paredObject;
223 }
224
225
226
227 /**
228 * Pare down an attribute to its requested sub-attributes.
229 *
230 * @param attribute The attribute to be pared down.
231 *
232 * @return The pared down attribute, or {@code null} if the attribute
233 * should not be included at all.
234 */
235 public SCIMAttribute pareAttribute(final SCIMAttribute attribute)
236 {
237 final AttributeDescriptor descriptor = attribute.getAttributeDescriptor();
238
239 if (allAttributesRequested() || descriptor.getSubAttributes() == null)
240 {
241 return attribute;
242 }
243
244 final Set<AttributeDescriptor> subDescriptors = descriptors.get(descriptor);
245 if (subDescriptors == null)
246 {
247 return null;
248 }
249
250 if (subDescriptors.isEmpty())
251 {
252 return attribute;
253 }
254
255 if (attribute.getAttributeDescriptor().isMultiValued())
256 {
257 final ArrayList<SCIMAttributeValue> values =
258 new ArrayList<SCIMAttributeValue>();
259
260 for (final SCIMAttributeValue v : attribute.getValues())
261 {
262 final ArrayList<SCIMAttribute> subAttributes =
263 new ArrayList<SCIMAttribute>();
264 for (final AttributeDescriptor d : subDescriptors)
265 {
266 final SCIMAttribute subAttribute = v.getAttribute(d.getName());
267 if (subAttribute != null)
268 {
269 subAttributes.add(subAttribute);
270 }
271 }
272 values.add(SCIMAttributeValue.createComplexValue(subAttributes));
273 }
274
275 return SCIMAttribute.create(
276 descriptor, values.toArray(new SCIMAttributeValue[values.size()]));
277 }
278 else
279 {
280 final ArrayList<SCIMAttribute> subAttributes =
281 new ArrayList<SCIMAttribute>();
282 for (final AttributeDescriptor d : subDescriptors)
283 {
284 final SCIMAttribute subAttribute =
285 attribute.getValue().getAttribute(d.getName());
286 if (subAttribute != null)
287 {
288 subAttributes.add(subAttribute);
289 }
290 }
291 return SCIMAttribute.create(descriptor,
292 SCIMAttributeValue.createComplexValue(subAttributes));
293 }
294 }
295
296
297
298 /**
299 * Return query attributes formed by merging these query attributes with the
300 * provided query attributes.
301 *
302 * @param that The query attributes to be merged with these query attributes
303 * to form new query attributes.
304 *
305 * @return The merged query attributes.
306 *
307 * @throws InvalidResourceException If the query attributes could not be
308 * merged.
309 */
310 public SCIMQueryAttributes merge(final SCIMQueryAttributes that)
311 throws InvalidResourceException
312 {
313 if (this.allAttributesRequested || that.allAttributesRequested)
314 {
315 return new SCIMQueryAttributes(null);
316 }
317
318 final Map<AttributeDescriptor,Set<AttributeDescriptor>> merged =
319 new HashMap<AttributeDescriptor, Set<AttributeDescriptor>>(
320 this.descriptors);
321
322 for (final Map.Entry<AttributeDescriptor,Set<AttributeDescriptor>> e :
323 that.descriptors.entrySet())
324 {
325 final AttributeDescriptor attributeDescriptor = e.getKey();
326 final Set<AttributeDescriptor> thatSet = e.getValue();
327
328 Set<AttributeDescriptor> thisSet = merged.get(attributeDescriptor);
329 if (thisSet == null)
330 {
331 merged.put(attributeDescriptor, thatSet);
332 }
333 else
334 {
335 if (!thisSet.isEmpty())
336 {
337 if (thatSet.isEmpty())
338 {
339 thisSet.clear();
340 }
341 else
342 {
343 thisSet.addAll(thatSet);
344 }
345 }
346 }
347 }
348
349 return new SCIMQueryAttributes(merged);
350 }
351
352
353
354 /**
355 * {@inheritDoc}
356 */
357 @Override
358 public String toString()
359 {
360 final StringBuilder sb = new StringBuilder();
361 sb.append("SCIMQueryAttributes");
362 sb.append("{allAttributesRequested=").append(allAttributesRequested);
363 sb.append(", descriptors=").append(descriptors);
364 sb.append('}');
365 return sb.toString();
366 }
367 }