001/* 002 * Copyright 2011-2016 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.data; 019 020import com.unboundid.scim.marshal.Marshaller; 021import com.unboundid.scim.schema.AttributeDescriptor; 022import com.unboundid.scim.schema.ResourceDescriptor; 023import com.unboundid.scim.sdk.ComplexValue; 024import com.unboundid.scim.sdk.InvalidResourceException; 025import com.unboundid.scim.sdk.SCIMAttribute; 026import com.unboundid.scim.sdk.SCIMAttributeValue; 027import com.unboundid.scim.sdk.SCIMConstants; 028import com.unboundid.scim.sdk.SCIMObject; 029import com.unboundid.scim.sdk.SCIMResponse; 030import com.unboundid.scim.sdk.SimpleValue; 031 032import java.io.OutputStream; 033import java.util.ArrayList; 034import java.util.Collection; 035import java.util.Collections; 036import java.util.Iterator; 037 038/** 039 * This class represents a SCIM resource. It could also be sub-typed for 040 * specific resource types (ie. Users or Groups) that provide convenience 041 * methods for accessing specific attribute values. 042 */ 043public class BaseResource implements SCIMResponse 044{ 045 /** 046 * A <code>ResourceFactory</code> for creating <code>BaseResource</code> 047 * instances. 048 */ 049 public static final ResourceFactory<BaseResource> BASE_RESOURCE_FACTORY = 050 new ResourceFactory<BaseResource>() { 051 /** 052 * {@inheritDoc} 053 */ 054 public BaseResource createResource( 055 final ResourceDescriptor resourceDescriptor, 056 final SCIMObject scimObject) { 057 return new BaseResource(resourceDescriptor, scimObject); 058 } 059 }; 060 061 private final ResourceDescriptor resourceDescriptor; 062 private final SCIMObject scimObject; 063 064 /** 065 * Construct a <code>BaseResource</code> with the specified 066 * <code>ResourceDescriptor</code> and backed by the given 067 * <code>SCIMObject</code>. 068 * 069 * @param resourceDescriptor The resource descriptor for this SCIM resource. 070 * @param scimObject The <code>SCIMObject</code> containing all the 071 * SCIM attributes and their values. 072 */ 073 public BaseResource(final ResourceDescriptor resourceDescriptor, 074 final SCIMObject scimObject) 075 { 076 this.resourceDescriptor = resourceDescriptor; 077 this.scimObject = scimObject; 078 } 079 080 /** 081 * Construct an empty <code>BaseResource</code> with the specified 082 * <code>ResourceDescriptor</code>. 083 * 084 * @param resourceDescriptor The resource descriptor for this SCIM resource. 085 */ 086 public BaseResource(final ResourceDescriptor resourceDescriptor) 087 { 088 this.resourceDescriptor = resourceDescriptor; 089 this.scimObject = new SCIMObject(); 090 } 091 092 /** 093 * Retrieves the <code>ResourceDescriptor</code> for this resource. 094 * 095 * @return The <code>ResourceDescriptor</code> for this resource. 096 */ 097 public ResourceDescriptor getResourceDescriptor() { 098 return resourceDescriptor; 099 } 100 101 /** 102 * Retrieves the <code>SCIMObject</code> wrapped by this resource. 103 * 104 * @return The <code>SCIMObject</code> wrapped by this resource. 105 */ 106 public SCIMObject getScimObject() { 107 return scimObject; 108 } 109 110 /** 111 * Retrieves the unique identifier for the SCIM Resource as defined by 112 * the Service Provider. 113 * 114 * @return The unique identifier for the SCIM Resource as defined by 115 * the Service Provider. 116 */ 117 public String getId() 118 { 119 return getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "id", 120 AttributeValueResolver.STRING_RESOLVER); 121 } 122 123 /** 124 * Sets the unique identifier for the SCIM Resource. 125 * 126 * @param id The unique identifier for the SCIM Resource. 127 */ 128 public void setId(final String id) 129 { 130 try { 131 setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "id", 132 AttributeValueResolver.STRING_RESOLVER, id); 133 } catch (InvalidResourceException e) { 134 // This should never happen as these are core attributes... 135 throw new RuntimeException(e); 136 } 137 } 138 139 /** 140 * Retrieves the unique identifier for the Resource as defined by the 141 * Service Consumer. 142 * 143 * @return The unique identifier for the Resource as defined by the Service 144 * Consumer. 145 */ 146 public String getExternalId() 147 { 148 return getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, 149 "externalId", AttributeValueResolver.STRING_RESOLVER); 150 } 151 152 /** 153 * Sets the unique identifier for the Resource as defined by the Service 154 * Consumer. 155 * 156 * @param externalId The unique identifier for the Resource as defined by the 157 * Service Consumer. 158 */ 159 public void setExternalId(final String externalId) 160 { 161 try { 162 setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "externalId", 163 AttributeValueResolver.STRING_RESOLVER, externalId); 164 } catch (InvalidResourceException e) { 165 // This should never happen as these are core attributes... 166 throw new RuntimeException(e); 167 } 168 } 169 170 171 /** 172 * Retrieves the metadata about the resource. 173 * 174 * @return The metadata about the resource. 175 */ 176 public Meta getMeta() 177 { 178 return getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "meta", 179 Meta.META_RESOLVER); 180 } 181 182 /** 183 * Sets the metadata about the resource. 184 * @param meta The metadata about the resource. 185 */ 186 public void setMeta(final Meta meta) 187 { 188 try { 189 setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "meta", 190 Meta.META_RESOLVER, meta); 191 } catch (InvalidResourceException e) { 192 // This should never happen as these are core attributes... 193 throw new RuntimeException(e); 194 } 195 } 196 197 /** 198 * Retrieves a singular attribute value. 199 * 200 * @param <T> The type of the resolved instance representing the value of 201 * sub-attribute. 202 * @param schema The schema URI of the attribute value to retrieve. 203 * @param name The name of the attribute value to retrieve. 204 * @param resolver The <code>AttributeValueResolver</code> the should be used 205 * to resolve the value to an instance. 206 * @return The resolved value instance or <code>null</code> if the specified 207 * attribute does not exist. 208 */ 209 public <T> T getSingularAttributeValue( 210 final String schema, final String name, 211 final AttributeValueResolver<T> resolver) 212 { 213 SCIMAttribute attribute = scimObject.getAttribute(schema, name); 214 if(attribute != null) 215 { 216 SCIMAttributeValue value = attribute.getValue(); 217 if(value != null) 218 { 219 return resolver.toInstance(value); 220 } 221 } 222 return null; 223 } 224 225 226 /** 227 * Sets a singular attribute value. 228 * 229 * @param <T> The type of the resolved instance representing the value of 230 * sub-attribute. 231 * @param schema The schema URI of the attribute value to retrieve. 232 * @param name The name of the attribute value to retrieve. 233 * @param resolver The <code>AttributeValueResolver</code> the should be used 234 * to resolve the instance to attribute value. 235 * @param value The value instance. 236 * @throws InvalidResourceException if the attribute is not defined by the 237 * resource. 238 */ 239 public <T> void setSingularAttributeValue( 240 final String schema, final String name, 241 final AttributeValueResolver<T> resolver, final T value) 242 throws InvalidResourceException 243 { 244 if(value == null) 245 { 246 scimObject.removeAttribute(schema, name); 247 return; 248 } 249 250 AttributeDescriptor attributeDescriptor = 251 getResourceDescriptor().getAttribute(schema, name); 252 253 scimObject.setAttribute(SCIMAttribute.create( 254 attributeDescriptor, resolver.fromInstance(attributeDescriptor, 255 value))); 256 } 257 258 /** 259 * Retrieves a multi-valued attribute value. 260 * 261 * @param <T> The type of the resolved instance representing the value of 262 * sub-attribute. 263 * @param schema The schema URI of the attribute value to retrieve. 264 * @param name The name of the attribute value to retrieve. 265 * @param resolver The <code>AttributeValueResolver</code> the should be used 266 * to resolve the value to an instance. 267 * @return The collection of resolved value instances or <code>null</code> if 268 * the specified attribute does not exist. 269 */ 270 public <T> Collection<T> getAttributeValues( 271 final String schema, final String name, 272 final AttributeValueResolver<T> resolver) 273 { 274 SCIMAttribute attribute = scimObject.getAttribute(schema, name); 275 if(attribute != null) 276 { 277 SCIMAttributeValue[] values = attribute.getValues(); 278 if(values != null) 279 { 280 Collection<T> entries = new ArrayList<T>(values.length); 281 for(SCIMAttributeValue v : values) 282 { 283 entries.add(resolver.toInstance(v)); 284 } 285 return entries; 286 } 287 } 288 return null; 289 } 290 291 292 /** 293 * Sets a multi-valued attribute value. 294 * 295 * @param <T> The type of the resolved instance representing the value of 296 * sub-attribute. 297 * @param schema The schema URI of the attribute value to retrieve. 298 * @param name The name of the attribute value to retrieve. 299 * @param resolver The <code>AttributeValueResolver</code> the should be used 300 * to resolve the instance to attribute value. 301 * @param values The value instances. 302 * @throws InvalidResourceException if the attribute is not defined by the 303 * resource. 304 */ 305 public <T> void setAttributeValues( 306 final String schema, final String name, 307 final AttributeValueResolver<T> resolver, final Collection<T> values) 308 throws InvalidResourceException 309 { 310 if(values == null) 311 { 312 scimObject.removeAttribute(schema, name); 313 return; 314 } 315 316 AttributeDescriptor attributeDescriptor = 317 getResourceDescriptor().getAttribute(schema, name); 318 319 SCIMAttributeValue[] entries = new SCIMAttributeValue[values.size()]; 320 321 int i = 0; 322 for(T value : values) 323 { 324 entries[i++] = resolver.fromInstance(attributeDescriptor, value); 325 } 326 327 scimObject.setAttribute( 328 SCIMAttribute.create(attributeDescriptor, entries)); 329 } 330 331 332 333 /** 334 * Set a simple attribute value with the specified name, using an 335 * <code>AttributeValueResolver</code> appropriate for the attribute 336 * data type. 337 * 338 * @param name the name of the attribute to be set, must be unique among all 339 * schemas in use by the Resource. 340 * @param value the new attribute value. 341 * @throws InvalidResourceException if no attribute with the specified name 342 * is defined on the Resource, if the name is ambiguous, or if the 343 * attribute is not of a simple data type. 344 */ 345 public void setSimpleAttribute( 346 final String name, 347 final SimpleValue value) throws InvalidResourceException { 348 349 setSimpleAttribute(findDescriptor(name), value); 350 351 } 352 353 354 /** 355 * Set a simple attribute value with the specified schema and name, 356 * using an <code>AttributeValueResolver</code> appropriate for 357 * the attribute data type. 358 * 359 * @param schema Schema URI of the attribute to be set. 360 * @param name the name of the attribute to be set. 361 * @param value the new attribute value. 362 * @throws InvalidResourceException if no attribute with the specified name 363 * is defined by the schema, or if the attribute is not of a simple data 364 * type. 365 */ 366 public void setSimpleAttribute( 367 final String schema, 368 final String name, 369 final SimpleValue value) throws InvalidResourceException { 370 371 setSimpleAttribute( 372 getResourceDescriptor().getAttribute(schema, name), value); 373 374 } 375 376 377 /** 378 * Set a complex attribute value with the specified name. 379 * 380 * @param name the name of the attribute to be set, must be unique among all 381 * schemas in use by the Resource. 382 * @param attributeValue ComplexValue object containing a map of 383 * sub-attribute names and values 384 * @throws InvalidResourceException if no attribute with the specified name 385 * is defined on the Resource, if the name is ambiguous, or if the 386 * attribute is not of a complex data type. 387 */ 388 public void setComplexAttribute( 389 final String name, 390 final ComplexValue attributeValue) 391 throws InvalidResourceException { 392 393 setComplexAttribute(findDescriptor(name), attributeValue); 394 } 395 396 397 /** 398 * Set a complex attribute value with the specified schema and name. 399 * 400 * @param schema Schema URI of the attribute to be set. 401 * @param name The name of the attribute to be set. 402 * @param attributeValue ComplexValue object containing a map of 403 * sub-attribute names and values. 404 * @throws InvalidResourceException if the name is not defined by the 405 * specified schema or the attribute is not of a complex data type. 406 */ 407 public void setComplexAttribute( 408 final String schema, 409 final String name, 410 final ComplexValue attributeValue) 411 throws InvalidResourceException { 412 413 setComplexAttribute( 414 getResourceDescriptor().getAttribute(schema, name), 415 attributeValue); 416 } 417 418 419 /** 420 * Set all values of a multi-valued attribute with the specified name. 421 * If the attribute already exists, all values are overwritten. 422 * 423 * @param name The name of the attribute to be set, must be unique among all 424 * schemas in use by the Resource. 425 * @param values A collection of complex attribute values. 426 * @throws InvalidResourceException if no attribute with the specified name 427 * is defined on the Resource, if the name is ambiguous, or if the 428 * attribute is not multi-valued. 429 */ 430 public void setMultiValuedAttribute( 431 final String name, 432 final Collection<ComplexValue> values) 433 throws InvalidResourceException { 434 435 setMultiValuedAttribute(findDescriptor(name), values); 436 } 437 438 439 440 /** 441 * Set all values of a multi-valued attribute with the specified schema and 442 * name. If the attribute already exists, all values are overwritten. 443 * 444 * @param schema Schema URI of the attribute to be set. 445 * @param name The name of the attribute to be set. 446 * @param values A collection of complex attribute values. 447 * @throws InvalidResourceException if the name is not defined by the 448 * specified schema or if the attribute is not multi-valued. 449 */ 450 public void setMultiValuedAttribute( 451 final String schema, 452 final String name, 453 final Collection<ComplexValue> values) 454 throws InvalidResourceException { 455 456 setMultiValuedAttribute( 457 getResourceDescriptor().getAttribute(schema, name), 458 values); 459 } 460 461 462 /** 463 * Add or replace a value on a multi-valued attribute with the specified 464 * name. If the attribute includes a canonical type sub-attribute 465 * then a value matching the type will be overwritten. Otherwise the new 466 * value is added to the value list of the attribute. 467 * 468 * @param name The name of the attribute to be set, must be unique among all 469 * schemas in use by the Resource. 470 * @param attributeValue the complex attribute value. 471 * @throws InvalidResourceException if no attribute with the specified name 472 * is defined on the Resource, if the name is ambiguous, or if the 473 * attribute is not multi-valued. 474 */ 475 public void addOrReplaceMultiValuedValue( 476 final String name, 477 final ComplexValue attributeValue) 478 throws InvalidResourceException { 479 480 addOrReplaceMultiValuedValue(findDescriptor(name), attributeValue); 481 } 482 483 484 /** 485 * Add or replace a value on a multi-valued attribute with the specified 486 * schema and name. If the attribute includes a canonical type sub-attribute 487 * then a value matching the type will be overwritten. Otherwise the new 488 * value is added to the value list of the attribute. 489 * 490 * @param schema Schema URI of the attribute to be set. 491 * @param name The name of the attribute to be set. 492 * @param attributeValue the complex attribute value . 493 * @throws InvalidResourceException if the attribute name is not 494 * defined by the specified schema or if the attribute is not 495 * multi-valued. 496 */ 497 public void addOrReplaceMultiValuedValue( 498 final String schema, 499 final String name, 500 final ComplexValue attributeValue) 501 throws InvalidResourceException { 502 503 addOrReplaceMultiValuedValue( 504 getResourceDescriptor().getAttribute(schema, name), 505 attributeValue); 506 } 507 508 509 510 /** 511 * Get a simple attribute with the specified name. 512 * 513 * @param name The name of the attribute to retrieve, must be unique among 514 * all schemas in use by the Resource. 515 * @return A SimpleValue, or null if the attribute is not present 516 * on this Resource instance. 517 * @throws InvalidResourceException if no attribute with the specified name 518 * is defined on the Resource, or if the name is ambiguous. 519 */ 520 public SimpleValue getSimpleAttributeValue(final String name) 521 throws InvalidResourceException { 522 523 return getSimpleAttributeValue(findDescriptor(name)); 524 } 525 526 527 /** 528 * Get a simple attribute with the specified name and schema. 529 * 530 * @param schema The schema URI of the attribute to retrieve. 531 * @param name The name of the attribute to retrieve. 532 * @return A SimpleValue, or null if the attribute is not present 533 * on this Resource instance. 534 * @throws InvalidResourceException if the attribute is not defined 535 * by the resource schema or is not of a simple type. 536 */ 537 public SimpleValue getSimpleAttributeValue( 538 final String schema, 539 final String name) 540 throws InvalidResourceException { 541 542 return getSimpleAttributeValue(getDescriptor(name, schema)); 543 } 544 545 546 /** 547 * Get a complex attribute with the specified name. 548 * 549 * @param name The name of the attribute to retrieve, must be unique among 550 * all schemas in use by the Resource. 551 * @return A ComplexValue, or null if the attribute is not present 552 * on this Resource instance. 553 * @throws InvalidResourceException if no attribute with the specified name 554 * is defined on the Resource, or if the name is ambiguous. 555 */ 556 public ComplexValue getComplexAttributeValue(final String name) 557 throws InvalidResourceException { 558 559 return getComplexAttributeValue(findDescriptor(name)); 560 } 561 562 563 /** 564 * Get a complex attribute with the specified name and schema. 565 * 566 * @param schema The schema URI of the attribute to retrieve. 567 * @param name The name of the attribute to retrieve. 568 * @return A ComplexValue, or null if the attribute is not present 569 * on this Resource instance. 570 * @throws InvalidResourceException if the attribute is not defined 571 * by the resource schema or is not complex. 572 */ 573 public ComplexValue getComplexAttributeValue( 574 final String schema, 575 final String name) 576 throws InvalidResourceException { 577 578 return getComplexAttributeValue(getDescriptor(name, schema)); 579 } 580 581 582 /** 583 * Get the values of a multi-valued attribute with the specified name. 584 * 585 * @param name The name of the attribute to retrieve, must be unique among 586 * all schemas in use by the Resource. 587 * @return A Collection of ComplexValue, one for each value instance, 588 * or null if the attribute is not present on this Resource instance. 589 * @throws InvalidResourceException if the attribute is not defined by 590 * this SCIMResource or if the attribute is not multi-valued. 591 */ 592 public Collection<ComplexValue> getMultiValuedAttribute( 593 final String name) throws InvalidResourceException { 594 595 return getAttributeValues(findDescriptor(name)); 596 } 597 598 599 /** 600 * Get the values of the multi-valued attribute specified by schema and 601 * attribute name. 602 * 603 * @param schema The schema URI of the attribute to retrieve. 604 * @param name The name of the attribute to retrieve. 605 * @return A Collection of ComplexValue, one for each value instance, 606 * or null if the attribute is not present on this Resource instance. 607 * @throws InvalidResourceException if the attribute is not defined by 608 * this SCIMResource or if the attribute is not multi-valued. 609 */ 610 public Collection<ComplexValue> getMultiValuedAttribute( 611 final String schema, 612 final String name) throws InvalidResourceException { 613 614 return getAttributeValues(getDescriptor(name, schema)); 615 } 616 617 618 /** 619 * {@inheritDoc} 620 */ 621 public void marshal(final Marshaller marshaller, 622 final OutputStream outputStream) 623 throws Exception { 624 marshaller.marshal(this, outputStream); 625 } 626 627 /** 628 * {@inheritDoc} 629 */ 630 @Override 631 public boolean equals(final Object o) { 632 if (this == o) { 633 return true; 634 } 635 if (!(o instanceof BaseResource)) { 636 return false; 637 } 638 639 BaseResource that = (BaseResource) o; 640 641 if (!resourceDescriptor.equals(that.resourceDescriptor)) { 642 return false; 643 } 644 if (!scimObject.equals(that.scimObject)) { 645 return false; 646 } 647 648 return true; 649 } 650 651 /** 652 * {@inheritDoc} 653 */ 654 @Override 655 public int hashCode() { 656 int result = resourceDescriptor.hashCode(); 657 result = 31 * result + scimObject.hashCode(); 658 return result; 659 } 660 661 662 663 /** 664 * {@inheritDoc} 665 */ 666 @Override 667 public String toString() 668 { 669 final StringBuilder sb = new StringBuilder(); 670 sb.append("BaseResource"); 671 sb.append("{resource=").append(resourceDescriptor.getSchema()); 672 sb.append(SCIMConstants.SEPARATOR_CHAR_QUALIFIED_ATTRIBUTE); 673 sb.append(resourceDescriptor.getName()); 674 sb.append(", scimObject=").append(scimObject); 675 sb.append('}'); 676 return sb.toString(); 677 } 678 679 680 /** 681 * Find the attribute with the given name but unspecified schema. 682 * @param attributeName the name of the attribute to find 683 * @return AttributeDescriptor for the found attribute 684 * @throws InvalidResourceException if the specified attribute name 685 * is not defined for this Resource, or if it is used by more than one 686 * attribute schema. 687 */ 688 private AttributeDescriptor findDescriptor(final String attributeName) 689 throws InvalidResourceException { 690 691 AttributeDescriptor attributeDescriptor = null; 692 693 for (String schema : getResourceDescriptor().getAttributeSchemas()) { 694 AttributeDescriptor d = 695 getResourceDescriptor().findAttribute(schema, attributeName); 696 if (d != null) { 697 if (attributeDescriptor == null) { 698 attributeDescriptor = d; 699 } 700 else { 701 throw new InvalidResourceException(String.format( 702 "Ambiguous attribute: %s is defined by more than one schema.", 703 attributeName)); 704 } 705 } 706 } 707 if (attributeDescriptor == null) { 708 throw new InvalidResourceException( 709 String.format("No attribute with name %s defined for this resource", 710 attributeName)); 711 } 712 return attributeDescriptor; 713 } 714 715 716 /** 717 * Get the simple attribute defined by the specified AttributeDescriptor. 718 * @param descriptor AttributeDescriptor object 719 * @return A SimpleValue object, or null if the attribute is not 720 * present on this SCIMResource instance. 721 * @throws InvalidResourceException if the attribute is not of a simple 722 * data type. 723 */ 724 private SimpleValue getSimpleAttributeValue( 725 final AttributeDescriptor descriptor) throws InvalidResourceException { 726 727 validateNotComplex(descriptor); 728 729 SCIMAttribute attribute = scimObject.getAttribute(descriptor.getSchema(), 730 descriptor.getName()); 731 if(attribute != null) { 732 SCIMAttributeValue value = attribute.getValue(); 733 if (value != null) { 734 return value.getValue(); 735 } 736 } 737 return null; 738 } 739 740 741 /** 742 * Get the complex attribute defined by the specified AttributeDescriptor. 743 * @param descriptor AttributeDescriptor object 744 * @return A Map of sub-attribute values, or null if the attribute is not 745 * present on this SCIMResource instance 746 * @throws InvalidResourceException if the attribute is not defined as 747 * complex. 748 */ 749 private ComplexValue getComplexAttributeValue( 750 final AttributeDescriptor descriptor) throws InvalidResourceException { 751 752 validateComplex(descriptor); 753 validateSingular(descriptor); 754 755 return getSingularAttributeValue( 756 descriptor.getSchema(), 757 descriptor.getName(), 758 AttributeValueResolver.COMPLEX_RESOLVER); 759 } 760 761 762 /** 763 * Get all values of a multi-valued attribute. 764 * @param descriptor AttributeDescriptor object 765 * @return collection of complex attribute values 766 * @throws InvalidResourceException if attribute is not multi-valued 767 */ 768 private Collection<ComplexValue> getAttributeValues( 769 final AttributeDescriptor descriptor) throws InvalidResourceException { 770 771 validateMultiValued(descriptor); 772 validateComplex(descriptor); // is this needed - multi implies complex? 773 774 return getAttributeValues( 775 descriptor.getSchema(), 776 descriptor.getName(), 777 AttributeValueResolver.COMPLEX_RESOLVER); 778 779 } 780 781 782 /** 783 * Set a simple attribute value. 784 * @param descriptor AttributeDescriptor object 785 * @param value the new attribute value 786 * @throws InvalidResourceException if the attribute is not simple. 787 */ 788 private void setSimpleAttribute( 789 final AttributeDescriptor descriptor, 790 final SimpleValue value) throws InvalidResourceException { 791 792 validateNotComplex(descriptor); 793 794 scimObject.setAttribute(SCIMAttribute.create(descriptor, 795 SCIMAttributeValue.createSimpleValue(value))); 796 } 797 798 799 /** 800 * Set a complex attribute value. 801 * @param descriptor AttributeDescriptor object 802 * @param attributeValue the ComplexAttributeValue to set 803 * @throws InvalidResourceException if the attribute is not complex. 804 */ 805 private void setComplexAttribute( 806 final AttributeDescriptor descriptor, 807 final ComplexValue attributeValue) 808 throws InvalidResourceException { 809 810 validateComplex(descriptor); 811 validateSingular(descriptor); 812 813 setSingularAttributeValue(descriptor.getSchema(), 814 descriptor.getName(), 815 AttributeValueResolver.COMPLEX_RESOLVER, 816 attributeValue); 817 } 818 819 820 /** 821 * Set all values of a multi-valued attribute. If the attribute already 822 * exists, all values are overwritten. If the attribute does not exist 823 * it is added. 824 * @param descriptor AttributeDescriptor object 825 * @param values A collection of complex attribute values. 826 * @throws InvalidResourceException if the name is not defined by the 827 * specified schema or if the attribute is not multi-valued. 828 */ 829 private void setMultiValuedAttribute( 830 final AttributeDescriptor descriptor, 831 final Collection<ComplexValue> values) 832 throws InvalidResourceException { 833 834 validateMultiValued(descriptor); 835 validateComplex(descriptor); // is this needed? 836 837 setAttributeValues(descriptor.getSchema(), 838 descriptor.getName(), 839 AttributeValueResolver.COMPLEX_RESOLVER, 840 values); 841 } 842 843 844 /** 845 * Add or replace a value on a multi-valued attribute. 846 * If the attribute includes a canonical type sub-attribute then a value 847 * matching the type will be overwritten. Otherwise the new value is added 848 * to the value list of the attribute. 849 * @param descriptor AttributeDescriptor object 850 * @param attributeValue the complex attribute vale, as a set of 851 * sub-attribute values. 852 * @throws InvalidResourceException if the attribute name is not 853 * defined by the SCIMResource or if the attribute is not 854 * multi-valued. 855 */ 856 private void addOrReplaceMultiValuedValue( 857 final AttributeDescriptor descriptor, 858 final ComplexValue attributeValue) 859 throws InvalidResourceException { 860 861 Collection<ComplexValue> currentValues = 862 getAttributeValues(descriptor); 863 if (currentValues == null) { 864 currentValues = Collections.singleton(attributeValue); 865 } 866 else { 867 String canonicalType = attributeValue.getStringValue("type"); 868 if (canonicalType != null) { 869 Iterator<ComplexValue> iterator = currentValues.iterator(); 870 while (iterator.hasNext()) { 871 ComplexValue value = iterator.next(); 872 if (value.containsKey("type") && 873 value.getStringValue("type").equals(canonicalType)) { 874 // replace this value with the input value 875 iterator.remove(); 876 break; 877 } 878 } 879 } 880 currentValues.add(attributeValue); 881 } 882 setAttributeValues(descriptor.getSchema(), 883 descriptor.getName(), 884 AttributeValueResolver.COMPLEX_RESOLVER, 885 currentValues); 886 } 887 888 889 890 /** 891 * Utility method to get the AttributeDescriptor for the specified attribute 892 * name and schema. 893 * @param name attribute name 894 * @param schema attribute schema URI 895 * @return AttributeDescriptor object 896 * @throws InvalidResourceException if the Resource does not define an 897 * attribute with the specified name and schema. 898 */ 899 private AttributeDescriptor getDescriptor( 900 final String name, 901 final String schema) throws InvalidResourceException { 902 903 AttributeDescriptor descriptor = 904 getResourceDescriptor().getAttribute(schema, name); 905 if (descriptor == null) { 906 throw new InvalidResourceException( 907 String.format("No attribute with name %s defined with schema %s", 908 name, schema) 909 ); 910 } 911 return descriptor; 912 } 913 914 915 /** 916 * Validate that an attribute is a complex attribute. 917 * @param descriptor AttributeDescriptor for the attribute 918 * @throws InvalidResourceException if the attribute is not complex. 919 */ 920 private void validateComplex(final AttributeDescriptor descriptor) 921 throws InvalidResourceException { 922 if (!descriptor.getDataType().equals( 923 AttributeDescriptor.DataType.COMPLEX)) { 924 throw new InvalidResourceException( 925 String.format("Attribute %s is not defined as complex.", 926 descriptor.getName())); 927 } 928 } 929 930 /** 931 * Validate that an attribute is not a complex attribute. 932 * @param descriptor AttributeDescriptor for the attribute 933 * @throws InvalidResourceException if the attribute is complex. 934 */ 935 private void validateNotComplex(final AttributeDescriptor descriptor) 936 throws InvalidResourceException { 937 938 if (descriptor.getDataType().equals(AttributeDescriptor.DataType.COMPLEX)) { 939 throw new InvalidResourceException( 940 String.format("Attribute %s is defined as complex.", 941 descriptor.getName())); 942 } 943 944 } 945 946 947 /** 948 * Validate that an attribute is multi-valued. 949 * @param descriptor AttributeDescriptor for the attribute 950 * @throws InvalidResourceException if the attribute is not multi-valued. 951 */ 952 private void validateMultiValued(final AttributeDescriptor descriptor) 953 throws InvalidResourceException { 954 955 if (!descriptor.isMultiValued()) { 956 throw new InvalidResourceException(String.format( 957 "Attribute %s is not defined as multi-valued.", 958 descriptor.getName())); 959 } 960 961 } 962 963 964 /** 965 * Validate that an attribute is singular. 966 * @param descriptor AttributeDescriptor for the attribute 967 * @throws InvalidResourceException if the attribute is multi-valued. 968 */ 969 private void validateSingular(final AttributeDescriptor descriptor) 970 throws InvalidResourceException { 971 if (descriptor.isMultiValued()) { 972 throw new InvalidResourceException(String.format( 973 "Attribute %s is not a singular attribute.", 974 descriptor.getName())); 975 } 976 } 977}