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}