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.data.AttributeValueResolver;
021import com.unboundid.scim.schema.AttributeDescriptor;
022
023import javax.xml.bind.DatatypeConverter;
024import java.util.ArrayList;
025import java.util.Collection;
026import java.util.Collections;
027import java.util.Date;
028import java.util.LinkedHashMap;
029import java.util.Map;
030
031
032
033/**
034 * This class represents a System for Cross-Domain Identity Management (SCIM)
035 * attribute value. Values are categorized as either Simple or Complex.
036 *
037 * <ul>
038 * <li>Simple values can be String, Boolean, DateTime, Integer or Binary.</li>
039 * <li>Complex values are composed of a set of subordinate SCIM attributes.</li>
040 * </ul>
041 */
042public final class SCIMAttributeValue
043{
044  /**
045   * The simple attribute value, or {@code null} if the attribute value is
046   * complex.
047   */
048  private final SimpleValue value;
049
050  /**
051   * The attributes comprising the complex value, keyed by the lower case
052   * name of the attribute, or {@code null} if the attribute value is simple.
053   */
054  private final Map<String,SCIMAttribute> attributes;
055
056
057
058  /**
059   * Create a new instance of a SCIM attribute value.
060   *
061   * @param value  The simple value.
062   */
063  public SCIMAttributeValue(final SimpleValue value)
064  {
065    this.value      = value;
066    this.attributes = null;
067  }
068
069
070
071  /**
072   * Create a new instance of a SCIM complex attribute value.
073   *
074   * @param attributes  The attributes comprising the complex value, keyed by
075   *                    the name of the attribute.
076   */
077  private SCIMAttributeValue(final Map<String,SCIMAttribute> attributes)
078  {
079    this.value = null;
080    this.attributes = attributes;
081  }
082
083
084
085  /**
086   * Create a new simple attribute value of the specified data type.
087   *
088   * @param dataType  The data type of the value.
089   * @param value     The string representation of the value.
090   *
091   * @return  The new simple attribute value.
092   */
093  public static SCIMAttributeValue createValue(
094      final AttributeDescriptor.DataType dataType,
095      final String value)
096  {
097    switch (dataType)
098    {
099      case BINARY:
100        return createBinaryValue(DatatypeConverter.parseBase64Binary(value));
101      default:
102        return createStringValue(value);
103    }
104  }
105
106
107
108  /**
109   * Create a new simple String attribute value.
110   *
111   * @param value  The String attribute value.
112   *
113   * @return  The new simple attribute.
114   */
115  public static SCIMAttributeValue createStringValue(final String value)
116  {
117    return new SCIMAttributeValue(new SimpleValue(value));
118  }
119
120
121
122  /**
123   * Create a new simple Boolean attribute value.
124   *
125   * @param value  The Boolean attribute value.
126   *
127   * @return  The new simple attribute.
128   */
129  public static SCIMAttributeValue createBooleanValue(final Boolean value)
130  {
131    return new SCIMAttributeValue(new SimpleValue(value));
132  }
133
134
135
136  /**
137   * Create a new simple Date attribute value.
138   *
139   * @param value  The Date attribute value.
140   *
141   * @return  The new simple attribute.
142   */
143  public static SCIMAttributeValue createDateValue(final Date value)
144  {
145    return new SCIMAttributeValue(new SimpleValue(value));
146  }
147
148
149
150  /**
151   * Create a new simple binary attribute value.
152   *
153   * @param value  The binary attribute value.
154   *
155   * @return  The new simple attribute.
156   */
157  public static SCIMAttributeValue createBinaryValue(final byte[] value)
158  {
159    return new SCIMAttributeValue(new SimpleValue(value));
160  }
161
162  /**
163   * Retrieves the value of a sub-attribute.
164   *
165   * @param <T>          The type of the resolved instance representing the
166   *                     value of sub-attribute.
167   * @param name         The name of the sub-attribute.
168   * @param resolver     The <code>AttributeValueResolver</code> that should
169   *                     be used to resolve the value into an instance.
170   * @return             The resolved instance representing the value of
171   *                     sub-attribute.
172   */
173  public <T> T getSubAttributeValue(final String name,
174                                    final AttributeValueResolver<T> resolver)
175  {
176    SCIMAttribute attribute = getAttribute(name);
177    if(attribute != null)
178    {
179      SCIMAttributeValue v = attribute.getValue();
180      if(v != null)
181      {
182        return resolver.toInstance(v);
183      }
184    }
185    return null;
186  }
187
188  /**
189   * Retrieves the value of a multi-valued sub-attribute value.
190   *
191   * @param <T>    The type of the resolved instance representing the value of
192   *               sub-attribute.
193   * @param name The name of the attribute value to retrieve.
194   * @param resolver The <code>AttributeValueResolver</code> the should be used
195   *                 to resolve the value to an instance.
196   * @return The collection of resolved value instances or <code>null</code> if
197   *         the specified attribute does not exist.
198   */
199  public <T> Collection<T> getSubAttributeValues(
200      final String name, final AttributeValueResolver<T> resolver)
201  {
202    SCIMAttribute attribute = getAttribute(name);
203    if(attribute != null)
204    {
205      SCIMAttributeValue[] values = attribute.getValues();
206      if(values != null)
207      {
208        Collection<T> entries = new ArrayList<T>(values.length);
209        for(SCIMAttributeValue v : values)
210        {
211          entries.add(resolver.toInstance(v));
212        }
213        return entries;
214      }
215    }
216    return null;
217  }
218
219
220
221  /**
222   * {@inheritDoc}
223   */
224  @Override
225  public String toString()
226  {
227    final StringBuilder sb = new StringBuilder();
228    sb.append("SCIMAttributeValue{");
229    if (value != null)
230    {
231      sb.append("value=").append(value);
232    }
233    else
234    {
235      sb.append("attributes=").append(attributes);
236    }
237    sb.append('}');
238    return sb.toString();
239  }
240
241
242
243  /**
244   * Create a new complex attribute value from the provided attributes.
245   *
246   * @param attributes  The attributes comprising the complex value.
247   *
248   * @return  The new complex attribute.
249   */
250  public static SCIMAttributeValue createComplexValue(
251      final SCIMAttribute ... attributes)
252  {
253    final Map<String,SCIMAttribute> map =
254        new LinkedHashMap<String, SCIMAttribute>();
255    for (final SCIMAttribute a : attributes)
256    {
257      final String lowerCaseName = StaticUtils.toLowerCase(a.getName());
258      if (map.containsKey(lowerCaseName))
259      {
260        throw new RuntimeException("Duplicate attribute " + a.getName() +
261                                   " in complex attribute value");
262      }
263      map.put(lowerCaseName, a);
264    }
265    return new SCIMAttributeValue(Collections.unmodifiableMap(map));
266  }
267
268
269
270  /**
271   * Create a new complex attribute value from a collection of attributes.
272   *
273   * @param attributes  The attributes comprising the complex value.
274   *
275   * @return  The new complex attribute value.
276   */
277  public static SCIMAttributeValue createComplexValue(
278      final Collection<SCIMAttribute> attributes)
279  {
280    final Map<String,SCIMAttribute> map =
281        new LinkedHashMap<String, SCIMAttribute>();
282    for (final SCIMAttribute a : attributes)
283    {
284      final String lowerCaseName = StaticUtils.toLowerCase(a.getName());
285      if (map.containsKey(lowerCaseName))
286      {
287        throw new RuntimeException("Duplicate attribute " + a.getName() +
288                                   " in complex attribute value");
289      }
290      map.put(lowerCaseName, a);
291    }
292    return new SCIMAttributeValue(Collections.unmodifiableMap(map));
293  }
294
295
296
297  /**
298   * Determines whether this attribute value is simple or complex.
299   *
300   * @return  {@code true} if this attribute value is complex, or {@code false}
301   *          otherwise.
302   */
303  public boolean isComplex()
304  {
305    return this.value == null;
306  }
307
308
309
310  /**
311   * Retrieves the simple value, or {@code null} if the attribute value is
312   * complex.
313   *
314   * @return  The simple value, or {@code null} if the attribute value is
315   * complex.
316   */
317  public SimpleValue getValue()
318  {
319    return value;
320  }
321
322
323
324  /**
325   * Retrieves the simple String value, or {@code null} if the attribute
326   * value is complex.
327   *
328   * @return  The simple String value, or {@code null} if the attribute
329   *          value is complex.
330   */
331  public String getStringValue()
332  {
333    return value.getStringValue();
334  }
335
336
337
338  /**
339   * Retrieves the simple Boolean value, or {@code null} if the attribute
340   * value is complex.
341   *
342   * @return  The simple Boolean value, or {@code null} if the attribute
343   *          value is complex.
344   */
345  public Boolean getBooleanValue()
346  {
347    return value.getBooleanValue();
348  }
349
350
351
352  /**
353   * Retrieves the simple Decimal value, or {@code null} if the attribute
354   * value is complex.
355   *
356   * @return  The simple Decimal value, or {@code null} if the attribute
357   *          value is complex.
358   */
359  public Double getDecimalValue()
360  {
361    return value.getDoubleValue();
362  }
363
364
365
366  /**
367   * Retrieves the simple Long value, or {@code null} if the attribute
368   * value is complex.
369   *
370   * @return  The simple Long value, or {@code null} if the attribute
371   *          value is complex.
372   */
373  public Long getIntegerValue()
374  {
375    return value.getLongValue();
376  }
377
378
379
380  /**
381   * Retrieves the simple Date value, or {@code null} if the attribute
382   * value is complex.
383   *
384   * @return  The simple Date value, or {@code null} if the attribute
385   *          value is complex.
386   */
387  public Date getDateValue()
388  {
389    return value.getDateValue();
390  }
391
392
393
394  /**
395   * Retrieves the simple Binary value, or {@code null} if the attribute
396   * value is complex.
397   *
398   * @return  The simple Binary value, or {@code null} if the attribute
399   *          value is complex.
400   */
401  public byte[] getBinaryValue()
402  {
403    return value.getBinaryValue();
404  }
405
406
407
408  /**
409   * Retrieves the attributes comprising the complex value, keyed by the lower
410   * case name of the attribute, or {@code null} if the attribute value is
411   * simple.
412   *
413   * @return  The attributes comprising the complex value.
414   */
415  public Map<String, SCIMAttribute> getAttributes()
416  {
417    return attributes;
418  }
419
420
421
422  /**
423   * Retrieves the attribute with the provided name from the complex value,
424   * or {@code null} if there is no such attribute or the attribute value is
425   * simple.
426   *
427   * @param attributeName  The name of the desired attribute.
428   *
429   * @return  The attribute with the provided name, or {@code null} if there
430   *          is no such attribute or the attribute value is simple.
431   */
432  public SCIMAttribute getAttribute(final String attributeName)
433  {
434    if (attributes != null)
435    {
436      return attributes.get(StaticUtils.toLowerCase(attributeName));
437    }
438    else
439    {
440      return null;
441    }
442  }
443
444
445
446  /**
447   * Indicates whether a complex value has an attribute with the provided name.
448   *
449   * @param attributeName  The attribute name for which to make the
450   *                       determination.
451   *
452   * @return  {@code true} if there is an attribute with the provided name,
453   *          {@code false} if there is no such attribute or this attribute
454   *          value is simple.
455   */
456  public boolean hasAttribute(final String attributeName)
457  {
458    if (attributes != null)
459    {
460      return attributes.containsKey(StaticUtils.toLowerCase(attributeName));
461    }
462    else
463    {
464      return false;
465    }
466  }
467
468
469  /**
470   * {@inheritDoc}
471   */
472  @Override
473  public boolean equals(final Object o) {
474    if (this == o) {
475      return true;
476    }
477    if (o == null || getClass() != o.getClass()) {
478      return false;
479    }
480
481    SCIMAttributeValue that = (SCIMAttributeValue) o;
482
483    if (attributes != null ? !attributes.equals(that.attributes) :
484        that.attributes != null) {
485      return false;
486    }
487    if (value != null ? !value.equals(that.value) : that.value != null) {
488      return false;
489    }
490
491    return true;
492  }
493
494
495  /**
496   * {@inheritDoc}
497   */
498  @Override
499  public int hashCode() {
500    int result = value != null ? value.hashCode() : 0;
501    result = 31 * result + (attributes != null ? attributes.hashCode() : 0);
502    return result;
503  }
504}