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.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.InvalidResourceException;
024import com.unboundid.scim.sdk.SCIMAttribute;
025import com.unboundid.scim.sdk.SCIMAttributeValue;
026import com.unboundid.scim.sdk.SCIMConstants;
027import com.unboundid.scim.sdk.SCIMObject;
028import com.unboundid.scim.sdk.SCIMResponse;
029
030import java.io.OutputStream;
031import java.util.ArrayList;
032import 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 */
039public 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}