001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * docs/licenses/cddl.txt
011 * or http://www.opensource.org/licenses/cddl1.php.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * docs/licenses/cddl.txt.  If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 *      Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 *      Copyright 2015-2021 Ping Identity Corporation
026 */
027
028package com.unboundid.directory.sdk.broker.types;
029
030import com.unboundid.util.NotExtensible;
031
032import java.util.ArrayList;
033import java.util.Arrays;
034import java.util.Collection;
035import java.util.Collections;
036import java.util.LinkedList;
037
038
039
040/**
041 * An attribute in a Store Adapter's native schema. Use
042 * StoreAttributeDefinition.Builder to create instances of this class.
043 */
044@NotExtensible()
045public final class StoreAttributeDefinition
046{
047  /**
048   * An enumeration of the data types for values.
049   */
050  public enum Type
051  {
052    /**
053     * String datatype.
054     */
055    STRING("string"),
056
057    /**
058     * Boolean datatype.
059     */
060    BOOLEAN("boolean"),
061
062    /**
063     * Decimal datatype.
064     */
065    DECIMAL("decimal"),
066
067    /**
068     * Integer datatype.
069     */
070    INTEGER("integer"),
071
072    /**
073     * Datetime datatype.
074     */
075    DATETIME("datetime"),
076
077    /**
078     * Binary datatype.
079     */
080    BINARY("binary"),
081
082    /**
083     * Reference datatype.
084     */
085    REFERENCE("reference"),
086
087    /**
088     * Complex datatype.
089     */
090    COMPLEX("complex");
091
092    private String name;
093
094    /**
095     * Constructs an attribute data type object.
096     * @param name the name of the data type.
097     */
098    Type(final String name)
099    {
100      this.name = name;
101    }
102
103    /**
104     * Gets the name of the type.
105     *
106     * @return the name of the type.
107     */
108    public String getName()
109    {
110      return name;
111    }
112
113    /**
114     * Gets the Type matching the given name.  Throws a runtime
115     * exception if the Type cannot be found because an invalid
116     * name was given.
117     *
118     * @param name the name of the type.
119     * @return the type matching the given name.
120     */
121    public static Type fromName(final String name)
122    {
123      for(Type type : Type.values())
124      {
125        if(type.getName().equals(name))
126        {
127          return type;
128        }
129      }
130
131      throw new RuntimeException("Unknown StoreAttributeDefinition datatype");
132    }
133  }
134
135  private final String name;
136  private final Type type;
137  private final String description;
138  private final Collection<StoreAttributeDefinition> subAttributes;
139  private final boolean multiValued;
140
141  /**
142   * Builder class to build an instance of StoreAttributeDefinition.
143   */
144  public static class Builder
145  {
146    /**
147     * The name of the attribute.
148     */
149    private String name;
150
151    /**
152     * The type of the attribute.  For the possible values, see:
153     * {@link StoreAttributeDefinition.Type}
154     */
155    private Type type;
156
157    /**
158     * The sub-attributes of a complex attribute.
159     */
160    private Collection<StoreAttributeDefinition> subAttributes;
161
162    /**
163     * A boolean value indicating whether or not this attribute can have
164     * multiple values.
165     */
166    private boolean multiValued;
167
168    /**
169     * The description of this attribute.
170     */
171    private String description;
172
173    /**
174     * Create a new builder.
175     */
176    public Builder()
177    {
178      type = Type.STRING;
179    }
180
181    /**
182     * Sets the attribute name.
183     *
184     * @param name the attribute name.
185     * @return this
186     */
187    public Builder setName(final String name)
188    {
189      this.name = name;
190      return this;
191    }
192
193    /**
194     * Sets the type of the attribute.
195     *
196     * @param type the type of the attribute.
197     * @return this.
198     */
199    public Builder setType(final Type type)
200    {
201      this.type = type;
202      return this;
203    }
204
205    /**
206     * Adds sub-attributes for a complex datatype attribute.
207     *
208     * @param subAttributes  The sub-attributes of the attribute.
209     * @return this.
210     */
211    public Builder addSubAttributes(
212        final StoreAttributeDefinition ... subAttributes)
213    {
214      if (subAttributes != null && subAttributes.length > 0)
215      {
216        if (this.subAttributes == null)
217        {
218          this.subAttributes = new LinkedList<StoreAttributeDefinition>();
219        }
220        this.subAttributes.addAll(Arrays.asList(subAttributes));
221      }
222      return this;
223    }
224
225    /**
226     * Sets a boolean indicating if the attribute is multi-valued.
227     *
228     * @param multiValued a boolean indicating if the attribute is multi-valued.
229     * @return this.
230     */
231    public Builder setMultiValued(final boolean multiValued)
232    {
233      this.multiValued = multiValued;
234      return this;
235    }
236
237    /**
238     * Sets the description of the attribute.
239     *
240     * @param description the description of the attribute.
241     * @return this.
242     */
243    public Builder setDescription(final String description)
244    {
245      this.description = description;
246      return this;
247    }
248
249    /**
250     * Clears all values in this builder, so that it can be reused.
251     *
252     * @return this.
253     */
254    public Builder clear()
255    {
256      this.name = null;
257      this.type = Type.STRING;
258      this.subAttributes = null;
259      this.multiValued = false;
260      this.description = null;
261      return this;
262    }
263
264    /**
265     * Builds a new StoreAttributeDefinition.
266     *
267     * @return a new StoreAttributeDefinition.
268     */
269    public StoreAttributeDefinition build()
270    {
271      return new StoreAttributeDefinition(
272          name,
273          type,
274          subAttributes,
275          multiValued,
276          description);
277    }
278  }
279
280  /**
281   * Create a new Attribute Definition.
282   *
283   * @param name The name of the attribute.
284   * @param type The attribute data type.
285   * @param subAttributes The sub-attributes of the attribute.
286   * @param multiValued A boolean indicating if the attribute is multi-valued.
287   * @param description The description of this attribute.
288   */
289  private StoreAttributeDefinition(
290      final String name,
291      final Type type,
292      final Collection<StoreAttributeDefinition> subAttributes,
293      final boolean multiValued,
294      final String description)
295  {
296    this.name = name;
297    this.type = type;
298    this.subAttributes = subAttributes == null ?
299        null : Collections.unmodifiableList(
300        new ArrayList<StoreAttributeDefinition>(subAttributes));
301    this.multiValued = multiValued;
302    this.description = description;
303  }
304
305  /**
306   * Determines if the attribute allows multiple values.
307   *
308   * @return true if the attribute is multivalued, or false if it is not.
309   */
310  public boolean isMultiValued()
311  {
312    return multiValued;
313  }
314
315  /**
316   * Gets the type of the value for this attribute.
317   *
318   * @return type of the value for this attribute.
319   */
320  public Type getType()
321  {
322    return type;
323  }
324
325  /**
326   * Gets the name of the attribute.
327   *
328   * @return the name of the attribute.
329   */
330  public String getName()
331  {
332    return name;
333  }
334
335  /**
336   * Gets the description of the attribute.
337   *
338   * @return the description of the attribute.
339   */
340  public String getDescription()
341  {
342    return description;
343  }
344
345  /**
346   * Gets the sub-attributes for a complex attribute.
347   *
348   * @return the sub-attributes for a complex attribute.
349   */
350  public Collection<StoreAttributeDefinition> getSubAttributes()
351  {
352    return subAttributes;
353  }
354
355  /**
356   * Gets a string representation of the attribute.
357   *
358   * @return a string representation of the attribute.
359   */
360  @Override
361  public String toString()
362  {
363    return toIndentedString("");
364  }
365
366  /**
367   * Called by toString.  This is used to format the output of the object
368   * a little to help readability.
369   *
370   * @param indent the string to use for each indent increment.  For example,
371   *               one might use "  " for a 2 space indent.
372   * @return a string representation of this attribute.
373   */
374  private String toIndentedString(final String indent)
375  {
376    StringBuilder builder = new StringBuilder();
377    builder.append(indent);
378    builder.append("Name: ");
379    builder.append(getName());
380    builder.append(" Description: ");
381    builder.append(getDescription());
382    builder.append(" isReadOnly: ");
383    builder.append(System.lineSeparator());
384    if(getSubAttributes() != null)
385    {
386      for (StoreAttributeDefinition a : getSubAttributes())
387      {
388        builder.append(a.toIndentedString(indent + "  "));
389      }
390    }
391    return builder.toString();
392  }
393
394  /**
395   * {@inheritDoc}
396   */
397  @Override
398  public boolean equals(final Object o)
399  {
400    if (this == o)
401    {
402      return true;
403    }
404    if (o == null || getClass() != o.getClass())
405    {
406      return false;
407    }
408
409    StoreAttributeDefinition that = (StoreAttributeDefinition) o;
410
411    if (multiValued != that.multiValued)
412    {
413      return false;
414    }
415    if (description != null ? !description.equals(that.description) :
416        that.description != null)
417    {
418      return false;
419    }
420    if (name != null ? !name.equals(that.name) : that.name != null)
421    {
422      return false;
423    }
424    if (subAttributes != null ? !subAttributes.equals(that.subAttributes) :
425        that.subAttributes != null)
426    {
427      return false;
428    }
429    if (type != null ? !type.equals(that.type) : that.type != null)
430    {
431      return false;
432    }
433
434    return true;
435  }
436
437  /**
438   * {@inheritDoc}
439   */
440  @Override
441  public int hashCode()
442  {
443    int result = name != null ? name.hashCode() : 0;
444    result = 31 * result + (type != null ? type.hashCode() : 0);
445    result = 31 * result + (subAttributes != null ?
446        subAttributes.hashCode() : 0);
447    result = 31 * result + (multiValued ? 1 : 0);
448    result = 31 * result + (description != null ? description.hashCode() : 0);
449    return result;
450  }
451}