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