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 2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027
028package org.opends.server.admin;
029import org.forgerock.i18n.LocalizableMessage;
030
031
032
033import static org.forgerock.util.Reject.ifNull;
034
035import java.util.EnumSet;
036import java.util.Locale;
037import java.util.MissingResourceException;
038import java.util.regex.Matcher;
039import java.util.regex.Pattern;
040import java.util.regex.PatternSyntaxException;
041
042
043
044/**
045 * String property definition.
046 */
047public final class StringPropertyDefinition extends PropertyDefinition<String> {
048
049  /**
050   * An interface for incrementally constructing string property
051   * definitions.
052   */
053  public static class Builder extends
054      AbstractBuilder<String, StringPropertyDefinition> {
055
056    /**
057     * Flag indicating whether values of this property are
058     * case-insensitive.
059     */
060    private boolean isCaseInsensitive = true;
061
062    /** Optional pattern which values of this property must match. */
063    private Pattern pattern;
064
065    /**
066     * Pattern usage which provides a user-friendly summary of the
067     * pattern if present.
068     */
069    private String patternUsage;
070
071
072
073    /** Private constructor. */
074    private Builder(AbstractManagedObjectDefinition<?, ?> d,
075        String propertyName) {
076      super(d, propertyName);
077    }
078
079
080
081    /**
082     * Set a flag indicating whether values of this property are
083     * case-insensitive.
084     *
085     * @param value
086     *          <code>true</code> if values are case-insensitive, or
087     *          <code>false</code> otherwise.
088     */
089    public final void setCaseInsensitive(boolean value) {
090      isCaseInsensitive = value;
091    }
092
093
094
095    /**
096     * Set the regular expression pattern which values of this
097     * property must match. By default there is no pattern defined.
098     *
099     * @param pattern
100     *          The regular expression pattern string, or
101     *          <code>null</code> if there is no pattern.
102     * @param patternUsage
103     *          A user-friendly usage string representing the pattern
104     *          which can be used in error messages and help (e.g. for
105     *          patterns which match a host/port combination, the
106     *          usage string "HOST:PORT" would be appropriate).
107     * @throws PatternSyntaxException
108     *           If the provided regular expression pattern has an
109     *           invalid syntax.
110     */
111    public final void setPattern(String pattern, String patternUsage)
112        throws PatternSyntaxException {
113      if (pattern == null) {
114        this.pattern = null;
115        this.patternUsage = null;
116      } else {
117        this.pattern = Pattern.compile(pattern);
118        this.patternUsage = patternUsage;
119      }
120    }
121
122
123
124    /** {@inheritDoc} */
125    @Override
126    protected StringPropertyDefinition buildInstance(
127        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
128        EnumSet<PropertyOption> options,
129        AdministratorAction adminAction,
130        DefaultBehaviorProvider<String> defaultBehavior) {
131      return new StringPropertyDefinition(d, propertyName, options,
132          adminAction, defaultBehavior, isCaseInsensitive, pattern,
133          patternUsage);
134    }
135
136  }
137
138
139
140  /**
141   * Create a string property definition builder.
142   *
143   * @param d
144   *          The managed object definition associated with this
145   *          property definition.
146   * @param propertyName
147   *          The property name.
148   * @return Returns the new string property definition builder.
149   */
150  public static Builder createBuilder(AbstractManagedObjectDefinition<?, ?> d,
151      String propertyName) {
152    return new Builder(d, propertyName);
153  }
154
155  /**
156   * Flag indicating whether values of this property are
157   * case-insensitive.
158   */
159  private final boolean isCaseInsensitive;
160
161  /** Optional pattern which values of this property must match. */
162  private final Pattern pattern;
163
164  /**
165   * Pattern usage which provides a user-friendly summary of the
166   * pattern if present.
167   */
168  private final String patternUsage;
169
170
171
172  /** Private constructor. */
173  private StringPropertyDefinition(AbstractManagedObjectDefinition<?, ?> d,
174      String propertyName, EnumSet<PropertyOption> options,
175      AdministratorAction adminAction,
176      DefaultBehaviorProvider<String> defaultBehavior,
177      boolean isCaseInsensitive, Pattern pattern, String patternUsage) {
178    super(d, String.class, propertyName, options, adminAction,
179        defaultBehavior);
180    this.isCaseInsensitive = isCaseInsensitive;
181    this.pattern = pattern;
182    this.patternUsage = patternUsage;
183  }
184
185
186
187  /** {@inheritDoc} */
188  @Override
189  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
190    return v.visitString(this, p);
191  }
192
193
194
195  /** {@inheritDoc} */
196  @Override
197  public <R, P> R accept(PropertyValueVisitor<R, P> v, String value, P p) {
198    return v.visitString(this, value, p);
199  }
200
201
202
203  /** {@inheritDoc} */
204  @Override
205  public String decodeValue(String value)
206      throws PropertyException {
207    ifNull(value);
208
209    try {
210      validateValue(value);
211    } catch (PropertyException e) {
212      throw PropertyException.illegalPropertyValueException(this, value);
213    }
214
215    return value;
216  }
217
218
219
220  /**
221   * Gets the optional regular expression pattern which values of this
222   * property must match.
223   *
224   * @return Returns the optional regular expression pattern which
225   *         values of this property must match, or <code>null</code>
226   *         if there is no pattern.
227   */
228  public Pattern getPattern() {
229    return pattern;
230  }
231
232
233
234  /**
235   * Gets the pattern synopsis of this string property definition in
236   * the default locale.
237   *
238   * @return Returns the pattern synopsis of this string property
239   *         definition in the default locale, or <code>null</code>
240   *         if there is no pattern synopsis (which is the case when
241   *         there is no pattern matching defined for this string
242   *         property definition).
243   */
244  public LocalizableMessage getPatternSynopsis() {
245    return getPatternSynopsis(Locale.getDefault());
246  }
247
248
249
250  /**
251   * Gets the optional pattern synopsis of this string property
252   * definition in the specified locale.
253   *
254   * @param locale
255   *          The locale.
256   * @return Returns the pattern synopsis of this string property
257   *         definition in the specified locale, or <code>null</code>
258   *         if there is no pattern synopsis (which is the case when
259   *         there is no pattern matching defined for this string
260   *         property definition).
261   */
262  public LocalizableMessage getPatternSynopsis(Locale locale) {
263    ManagedObjectDefinitionI18NResource resource =
264      ManagedObjectDefinitionI18NResource.getInstance();
265    String property = "property." + getName()
266        + ".syntax.string.pattern.synopsis";
267    try {
268      return resource
269          .getMessage(getManagedObjectDefinition(), property, locale);
270    } catch (MissingResourceException e) {
271      return null;
272    }
273  }
274
275
276
277  /**
278   * Gets a user-friendly usage string representing the pattern which
279   * can be used in error messages and help (e.g. for patterns which
280   * match a host/port combination, the usage string "HOST:PORT" would
281   * be appropriate).
282   *
283   * @return Returns the user-friendly pattern usage string, or
284   *         <code>null</code> if there is no pattern.
285   */
286  public String getPatternUsage() {
287    return patternUsage;
288  }
289
290
291
292  /**
293   * Query whether values of this property are case-insensitive.
294   *
295   * @return Returns <code>true</code> if values are
296   *         case-insensitive, or <code>false</code> otherwise.
297   */
298  public boolean isCaseInsensitive() {
299    return isCaseInsensitive;
300  }
301
302
303
304  /** {@inheritDoc} */
305  @Override
306  public String normalizeValue(String value)
307      throws PropertyException {
308    ifNull(value);
309
310    if (isCaseInsensitive()) {
311      return value.trim().toLowerCase();
312    } else {
313      return value.trim();
314    }
315  }
316
317
318
319  /** {@inheritDoc} */
320  @Override
321  public void validateValue(String value) throws PropertyException {
322    ifNull(value);
323
324    if (pattern != null) {
325      Matcher matcher = pattern.matcher(value);
326      if (!matcher.matches()) {
327        throw PropertyException.illegalPropertyValueException(this, value);
328      }
329    }
330  }
331}