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}