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