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.data;
019    
020    import com.unboundid.scim.marshal.Marshaller;
021    import com.unboundid.scim.schema.AttributeDescriptor;
022    import com.unboundid.scim.schema.ResourceDescriptor;
023    import com.unboundid.scim.sdk.InvalidResourceException;
024    import com.unboundid.scim.sdk.SCIMAttribute;
025    import com.unboundid.scim.sdk.SCIMAttributeValue;
026    import com.unboundid.scim.sdk.SCIMConstants;
027    import com.unboundid.scim.sdk.SCIMObject;
028    import com.unboundid.scim.sdk.SCIMResponse;
029    
030    import java.io.OutputStream;
031    import java.util.ArrayList;
032    import java.util.Collection;
033    
034    /**
035     * This class represents a SCIM resource. It could also be sub-typed for
036     * specific resource types (ie. Users or Groups) that provide convenience
037     * methods for accessing specific attribute values.
038     */
039    public class BaseResource implements SCIMResponse
040    {
041      /**
042       * A <code>ResourceFactory</code> for creating <code>BaseResource</code>
043       * instances.
044       */
045      public static final ResourceFactory<BaseResource> BASE_RESOURCE_FACTORY =
046          new ResourceFactory<BaseResource>() {
047            /**
048             * {@inheritDoc}
049             */
050            public BaseResource createResource(
051                final ResourceDescriptor resourceDescriptor,
052                final SCIMObject scimObject) {
053              return new BaseResource(resourceDescriptor, scimObject);
054            }
055          };
056    
057      private final ResourceDescriptor resourceDescriptor;
058      private final SCIMObject scimObject;
059    
060      /**
061       * Construct a <code>BaseResource</code> with the specified
062       * <code>ResourceDescriptor</code> and backed by the given
063       * <code>SCIMObject</code>.
064       *
065       * @param resourceDescriptor The resource descriptor for this SCIM resource.
066       * @param scimObject         The <code>SCIMObject</code> containing all the
067       *                           SCIM attributes and their values.
068       */
069      public BaseResource(final ResourceDescriptor resourceDescriptor,
070                          final SCIMObject scimObject)
071      {
072        this.resourceDescriptor = resourceDescriptor;
073        this.scimObject = scimObject;
074      }
075    
076      /**
077       * Construct an empty <code>BaseResource</code> with the specified
078       * <code>ResourceDescriptor</code>.
079       *
080       * @param resourceDescriptor The resource descriptor for this SCIM resource.
081       */
082      public BaseResource(final ResourceDescriptor resourceDescriptor)
083      {
084        this.resourceDescriptor = resourceDescriptor;
085        this.scimObject = new SCIMObject();
086      }
087    
088      /**
089       * Retrieves the <code>ResourceDescriptor</code> for this resource.
090       *
091       * @return The <code>ResourceDescriptor</code> for this resource.
092       */
093      public ResourceDescriptor getResourceDescriptor() {
094        return resourceDescriptor;
095      }
096    
097      /**
098       * Retrieves the <code>SCIMObject</code> wrapped by this resource.
099       *
100       * @return The <code>SCIMObject</code> wrapped by this resource.
101       */
102      public SCIMObject getScimObject() {
103        return scimObject;
104      }
105    
106      /**
107       * Retrieves the unique identifier for the SCIM Resource as defined by
108       * the Service Provider.
109       *
110       * @return The unique identifier for the SCIM Resource as defined by
111       * the Service Provider.
112       */
113      public String getId()
114      {
115        return getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "id",
116            AttributeValueResolver.STRING_RESOLVER);
117      }
118    
119      /**
120       * Sets the unique identifier for the SCIM Resource.
121       *
122       * @param id The unique identifier for the SCIM Resource.
123       */
124      public void setId(final String id)
125      {
126        try {
127          setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "id",
128              AttributeValueResolver.STRING_RESOLVER, id);
129        } catch (InvalidResourceException e) {
130          // This should never happen as these are core attributes...
131          throw new RuntimeException(e);
132        }
133      }
134    
135      /**
136       * Retrieves the unique identifier for the Resource as defined by the
137       * Service Consumer.
138       *
139       * @return The unique identifier for the Resource as defined by the Service
140       * Consumer.
141       */
142      public String getExternalId()
143      {
144        return getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE,
145            "externalId", AttributeValueResolver.STRING_RESOLVER);
146      }
147    
148      /**
149       * Sets the unique identifier for the Resource as defined by the Service
150       * Consumer.
151       *
152       * @param externalId The unique identifier for the Resource as defined by the
153       * Service Consumer.
154       */
155      public void setExternalId(final String externalId)
156      {
157        try {
158          setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "externalId",
159              AttributeValueResolver.STRING_RESOLVER, externalId);
160        } catch (InvalidResourceException e) {
161          // This should never happen as these are core attributes...
162          throw new RuntimeException(e);
163        }
164      }
165    
166    
167      /**
168       * Retrieves the metadata about the resource.
169       *
170       * @return The metadata about the resource.
171       */
172      public Meta getMeta()
173      {
174        return getSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "meta",
175            Meta.META_RESOLVER);
176      }
177    
178      /**
179       * Sets the metadata about the resource.
180       * @param meta The metadata about the resource.
181       */
182      public void setMeta(final Meta meta)
183      {
184        try {
185          setSingularAttributeValue(SCIMConstants.SCHEMA_URI_CORE, "meta",
186              Meta.META_RESOLVER, meta);
187        } catch (InvalidResourceException e) {
188          // This should never happen as these are core attributes...
189          throw new RuntimeException(e);
190        }
191      }
192    
193      /**
194       * Retrieves a singular attribute value.
195       *
196       * @param <T>    The type of the resolved instance representing the value of
197       *               sub-attribute.
198       * @param schema The schema URI of the attribute value to retrieve.
199       * @param name The name of the attribute value to retrieve.
200       * @param resolver The <code>AttributeValueResolver</code> the should be used
201       *                 to resolve the value to an instance.
202       * @return The resolved value instance or <code>null</code> if the specified
203       *         attribute does not exist.
204       */
205      public <T> T getSingularAttributeValue(
206          final String schema, final String name,
207          final AttributeValueResolver<T> resolver)
208      {
209        SCIMAttribute attribute = scimObject.getAttribute(schema, name);
210        if(attribute != null)
211        {
212          SCIMAttributeValue value = attribute.getValue();
213          if(value != null)
214          {
215            return resolver.toInstance(value);
216          }
217        }
218        return null;
219      }
220    
221      /**
222       * Sets a singular attribute value.
223       *
224       * @param <T>    The type of the resolved instance representing the value of
225       *               sub-attribute.
226       * @param schema The schema URI of the attribute value to retrieve.
227       * @param name The name of the attribute value to retrieve.
228       * @param resolver The <code>AttributeValueResolver</code> the should be used
229       *                 to resolve the instance to attribute value.
230       * @param value The value instance.
231       * @throws InvalidResourceException if the attribute is not defined by the
232       *                                  resource.
233       */
234      public <T> void setSingularAttributeValue(
235          final String schema, final String name,
236          final AttributeValueResolver<T> resolver, final T value)
237          throws InvalidResourceException
238      {
239        if(value == null)
240        {
241          scimObject.removeAttribute(schema, name);
242          return;
243        }
244    
245        AttributeDescriptor attributeDescriptor =
246            getResourceDescriptor().getAttribute(schema, name);
247    
248        scimObject.setAttribute(SCIMAttribute.create(
249            attributeDescriptor, resolver.fromInstance(attributeDescriptor,
250            value)));
251      }
252    
253      /**
254       * Retrieves a multi-valued attribute value.
255       *
256       * @param <T>    The type of the resolved instance representing the value of
257       *               sub-attribute.
258       * @param schema The schema URI of the attribute value to retrieve.
259       * @param name The name of the attribute value to retrieve.
260       * @param resolver The <code>AttributeValueResolver</code> the should be used
261       *                 to resolve the value to an instance.
262       * @return The collection of resolved value instances or <code>null</code> if
263       *         the specified attribute does not exist.
264       */
265      public <T> Collection<T> getAttributeValues(
266          final String schema, final String name,
267          final AttributeValueResolver<T> resolver)
268      {
269        SCIMAttribute attribute = scimObject.getAttribute(schema, name);
270        if(attribute != null)
271        {
272          SCIMAttributeValue[] values = attribute.getValues();
273          if(values != null)
274          {
275            Collection<T> entries = new ArrayList<T>(values.length);
276            for(SCIMAttributeValue v : values)
277            {
278              entries.add(resolver.toInstance(v));
279            }
280            return entries;
281          }
282        }
283        return null;
284      }
285    
286      /**
287       * Sets a multi-valued attribute value.
288       *
289       * @param <T>    The type of the resolved instance representing the value of
290       *               sub-attribute.
291       * @param schema The schema URI of the attribute value to retrieve.
292       * @param name The name of the attribute value to retrieve.
293       * @param resolver The <code>AttributeValueResolver</code> the should be used
294       *                 to resolve the instance to attribute value.
295       * @param values The value instances.
296       * @throws InvalidResourceException if the attribute is not defined by the
297       *                                  resource.
298       */
299      public <T> void setAttributeValues(
300          final String schema, final String name,
301          final AttributeValueResolver<T> resolver, final Collection<T> values)
302          throws InvalidResourceException
303      {
304        if(values == null)
305        {
306          scimObject.removeAttribute(schema, name);
307          return;
308        }
309    
310        AttributeDescriptor attributeDescriptor =
311            getResourceDescriptor().getAttribute(schema, name);
312    
313        SCIMAttributeValue[] entries = new SCIMAttributeValue[values.size()];
314    
315        int i = 0;
316        for(T value : values)
317        {
318          entries[i++] = resolver.fromInstance(attributeDescriptor, value);
319        }
320    
321        scimObject.setAttribute(
322            SCIMAttribute.create(attributeDescriptor, entries));
323      }
324    
325      /**
326       * {@inheritDoc}
327       */
328      public void marshal(final Marshaller marshaller,
329                          final OutputStream outputStream)
330          throws Exception {
331        marshaller.marshal(this, outputStream);
332      }
333    
334      /**
335       * {@inheritDoc}
336       */
337      @Override
338      public boolean equals(final Object o) {
339        if (this == o) {
340          return true;
341        }
342        if (!(o instanceof BaseResource)) {
343          return false;
344        }
345    
346        BaseResource that = (BaseResource) o;
347    
348        if (!resourceDescriptor.equals(that.resourceDescriptor)) {
349          return false;
350        }
351        if (!scimObject.equals(that.scimObject)) {
352          return false;
353        }
354    
355        return true;
356      }
357    
358      /**
359       * {@inheritDoc}
360       */
361      @Override
362      public int hashCode() {
363        int result = resourceDescriptor.hashCode();
364        result = 31 * result + scimObject.hashCode();
365        return result;
366      }
367    
368    
369    
370      /**
371       * {@inheritDoc}
372       */
373      @Override
374      public String toString()
375      {
376        final StringBuilder sb = new StringBuilder();
377        sb.append("BaseResource");
378        sb.append("{resource=").append(resourceDescriptor.getSchema());
379        sb.append(SCIMConstants.SEPARATOR_CHAR_QUALIFIED_ATTRIBUTE);
380        sb.append(resourceDescriptor.getName());
381        sb.append(", scimObject=").append(scimObject);
382        sb.append('}');
383        return sb.toString();
384      }
385    }