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