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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.types;
028
029import org.forgerock.i18n.LocalizableMessage;
030
031
032import java.security.SecureRandom;
033import java.util.Random;
034import java.util.SortedSet;
035
036import org.forgerock.opendj.config.server.ConfigException;
037
038import static org.opends.messages.UtilityMessages.*;
039import static org.opends.server.util.StaticUtils.*;
040
041
042
043/**
044 * This class provides a data structure that makes it possible to
045 * associate a name with a given set of characters.  The name must
046 * consist only of ASCII alphabetic characters.
047 */
048@org.opends.server.types.PublicAPI(
049     stability=org.opends.server.types.StabilityLevel.VOLATILE,
050     mayInstantiate=true,
051     mayExtend=false,
052     mayInvoke=true)
053public final class NamedCharacterSet
054{
055  /** The characters contained in this character set. */
056  private final char[] characters;
057
058  /** The random number generator to use with this character set. */
059  private final Random random;
060
061  /** The name assigned to this character set. */
062  private final String name;
063
064
065
066  /**
067   * Creates a new named character set with the provided information.
068   *
069   * @param  name        The name for this character set.
070   * @param  characters  The characters to include in this character
071   *                     set.
072   *
073   * @throws  ConfigException  If the provided name contains one or
074   *                           more illegal characters.
075   */
076  public NamedCharacterSet(String name, char[] characters)
077         throws ConfigException
078  {
079    this(name, characters, new SecureRandom());
080  }
081
082
083
084  /**
085   * Creates a new named character set with the provided information.
086   *
087   * @param  name        The name for this character set.
088   * @param  characters  The characters to include in this character
089   *                     set.
090   * @param  random      The random number generator to use with this
091   *                     character set.
092   *
093   * @throws  ConfigException  If the provided name contains one or
094   *                           more illegal characters.
095   */
096  public NamedCharacterSet(String name, char[] characters,
097                           Random random)
098         throws ConfigException
099  {
100    this.name       = name;
101    this.characters = characters;
102    this.random     = random;
103
104    if (name == null || name.length() == 0)
105    {
106      LocalizableMessage message = ERR_CHARSET_CONSTRUCTOR_NO_NAME.get();
107      throw new ConfigException(message);
108    }
109
110    for (int i=0; i < name.length(); i++)
111    {
112      if (! isAlpha(name.charAt(i)))
113      {
114        throw new ConfigException(ERR_CHARSET_CONSTRUCTOR_INVALID_NAME_CHAR.get(name.charAt(i), i));
115      }
116    }
117  }
118
119
120
121  /**
122   * Retrieves the name for this character set.
123   *
124   * @return  The name for this character set.
125   */
126  public String getName()
127  {
128    return name;
129  }
130
131
132
133  /**
134   * Retrieves the characters included in this character set.
135   *
136   * @return  The characters included in this character set.
137   */
138  public char[] getCharacters()
139  {
140    return characters;
141  }
142
143
144
145  /**
146   * Retrieves a character at random from this named character set.
147   *
148   * @return  The randomly-selected character from this named
149   *          character set;
150   */
151  public char getRandomCharacter()
152  {
153    if (characters == null || characters.length == 0)
154    {
155      return 0;
156    }
157
158    return characters[random.nextInt(characters.length)];
159  }
160
161
162
163  /**
164   * Appends the specified number of characters chosen at random from
165   * this character set to the provided buffer.
166   *
167   * @param  buffer  The buffer to which the characters should be
168   *                 appended.
169   * @param  count   The number of characters to append to the
170   *                 provided buffer.
171   */
172  public void getRandomCharacters(StringBuilder buffer, int count)
173  {
174    if (characters == null || characters.length == 0)
175    {
176      return;
177    }
178
179    for (int i=0; i < count; i++)
180    {
181      buffer.append(characters[random.nextInt(characters.length)]);
182    }
183  }
184
185
186
187  /**
188   * Encodes this character set to a form suitable for use in the
189   * value of a configuration attribute.
190   *
191   * @return  The encoded character set in a form suitable for use in
192   *          the value of a configuration attribute.
193   */
194  public String encode()
195  {
196    return name + ":" + new String(characters);
197  }
198
199
200
201  /**
202   * Decodes the values of the provided configuration attribute as a
203   * set of character set definitions.
204   *
205   * @param  values  The set of encoded character set values to
206   *                 decode.
207   *
208   * @return  The decoded character set definitions.
209   *
210   * @throws  ConfigException  If a problem occurs while attempting to
211   *                           decode the character set definitions.
212   */
213  public static NamedCharacterSet[]
214                     decodeCharacterSets(SortedSet<String> values)
215         throws ConfigException
216  {
217    NamedCharacterSet[] sets = new NamedCharacterSet[values.size()];
218    int i = 0 ;
219    for (String value : values)
220    {
221      int colonPos = value.indexOf(':');
222      if (colonPos < 0)
223      {
224        throw new ConfigException(ERR_CHARSET_NO_COLON.get(value));
225      }
226      else if (colonPos == 0)
227      {
228        throw new ConfigException(ERR_CHARSET_NO_NAME.get(value));
229      }
230      else if (colonPos == (value.length() - 1))
231      {
232        throw new ConfigException(ERR_CHARSET_NO_CHARS.get(value));
233      }
234      else
235      {
236        String name       = value.substring(0, colonPos);
237        char[] characters = value.substring(colonPos+1).toCharArray();
238        sets[i] = new NamedCharacterSet(name, characters);
239      }
240      i++;
241    }
242
243    return sets;
244  }
245}
246