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