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
032
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 char[] characters;
057
058  /** The random number generator to use with this character set. */
059  private Random random;
060
061  /** The name assigned to this character set. */
062  private 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       = name;
080    this.characters = characters;
081
082    random     = new Random();
083
084    if (name == null || name.length() == 0)
085    {
086      LocalizableMessage message = ERR_CHARSET_CONSTRUCTOR_NO_NAME.get();
087      throw new ConfigException(message);
088    }
089
090    for (int i=0; i < name.length(); i++)
091    {
092      if (! isAlpha(name.charAt(i)))
093      {
094        throw new ConfigException(ERR_CHARSET_CONSTRUCTOR_INVALID_NAME_CHAR.get(name.charAt(i), i));
095      }
096    }
097  }
098
099
100
101  /**
102   * Creates a new named character set with the provided information.
103   *
104   * @param  name        The name for this character set.
105   * @param  characters  The characters to include in this character
106   *                     set.
107   * @param  random      The random number generator to use with this
108   *                     character set.
109   *
110   * @throws  ConfigException  If the provided name contains one or
111   *                           more illegal characters.
112   */
113  public NamedCharacterSet(String name, char[] characters,
114                           Random random)
115         throws ConfigException
116  {
117    this.name       = name;
118    this.characters = characters;
119    this.random     = random;
120
121    if (name == null || name.length() == 0)
122    {
123      LocalizableMessage message = ERR_CHARSET_CONSTRUCTOR_NO_NAME.get();
124      throw new ConfigException(message);
125    }
126
127    for (int i=0; i < name.length(); i++)
128    {
129      if (! isAlpha(name.charAt(i)))
130      {
131        throw new ConfigException(ERR_CHARSET_CONSTRUCTOR_INVALID_NAME_CHAR.get(name.charAt(i), i));
132      }
133    }
134  }
135
136
137
138  /**
139   * Retrieves the name for this character set.
140   *
141   * @return  The name for this character set.
142   */
143  public String getName()
144  {
145    return name;
146  }
147
148
149
150  /**
151   * Retrieves the characters included in this character set.
152   *
153   * @return  The characters included in this character set.
154   */
155  public char[] getCharacters()
156  {
157    return characters;
158  }
159
160
161
162  /**
163   * Retrieves a character at random from this named character set.
164   *
165   * @return  The randomly-selected character from this named
166   *          character set;
167   */
168  public char getRandomCharacter()
169  {
170    if (characters == null || characters.length == 0)
171    {
172      return 0;
173    }
174
175    return characters[random.nextInt(characters.length)];
176  }
177
178
179
180  /**
181   * Appends the specified number of characters chosen at random from
182   * this character set to the provided buffer.
183   *
184   * @param  buffer  The buffer to which the characters should be
185   *                 appended.
186   * @param  count   The number of characters to append to the
187   *                 provided buffer.
188   */
189  public void getRandomCharacters(StringBuilder buffer, int count)
190  {
191    if (characters == null || characters.length == 0)
192    {
193      return;
194    }
195
196    for (int i=0; i < count; i++)
197    {
198      buffer.append(characters[random.nextInt(characters.length)]);
199    }
200  }
201
202
203
204  /**
205   * Encodes this character set to a form suitable for use in the
206   * value of a configuration attribute.
207   *
208   * @return  The encoded character set in a form suitable for use in
209   *          the value of a configuration attribute.
210   */
211  public String encode()
212  {
213    return name + ":" + new String(characters);
214  }
215
216
217
218  /**
219   * Decodes the values of the provided configuration attribute as a
220   * set of character set definitions.
221   *
222   * @param  values  The set of encoded character set values to
223   *                 decode.
224   *
225   * @return  The decoded character set definitions.
226   *
227   * @throws  ConfigException  If a problem occurs while attempting to
228   *                           decode the character set definitions.
229   */
230  public static NamedCharacterSet[]
231                     decodeCharacterSets(SortedSet<String> values)
232         throws ConfigException
233  {
234    NamedCharacterSet[] sets = new NamedCharacterSet[values.size()];
235    int i = 0 ;
236    for (String value : values)
237    {
238      int colonPos = value.indexOf(':');
239      if (colonPos < 0)
240      {
241        throw new ConfigException(ERR_CHARSET_NO_COLON.get(value));
242      }
243      else if (colonPos == 0)
244      {
245        throw new ConfigException(ERR_CHARSET_NO_NAME.get(value));
246      }
247      else if (colonPos == (value.length() - 1))
248      {
249        throw new ConfigException(ERR_CHARSET_NO_CHARS.get(value));
250      }
251      else
252      {
253        String name       = value.substring(0, colonPos);
254        char[] characters = value.substring(colonPos+1).toCharArray();
255        sets[i] = new NamedCharacterSet(name, characters);
256      }
257      i++;
258    }
259
260    return sets;
261  }
262}
263