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