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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.schema; 028 import org.opends.messages.Message; 029 030 031 032 import java.util.List; 033 034 import org.opends.server.admin.server.ConfigurationChangeListener; 035 import org.opends.server.admin.std.server.TelephoneNumberAttributeSyntaxCfg; 036 import org.opends.server.api.ApproximateMatchingRule; 037 import org.opends.server.api.AttributeSyntax; 038 import org.opends.server.api.EqualityMatchingRule; 039 import org.opends.server.api.OrderingMatchingRule; 040 import org.opends.server.api.SubstringMatchingRule; 041 import org.opends.server.config.ConfigException; 042 import org.opends.server.core.DirectoryServer; 043 import org.opends.server.types.ByteString; 044 import org.opends.server.types.ConfigChangeResult; 045 046 047 import org.opends.server.types.ResultCode; 048 049 import static org.opends.server.loggers.ErrorLogger.*; 050 import static org.opends.messages.SchemaMessages.*; 051 052 import org.opends.messages.MessageBuilder; 053 import static org.opends.server.schema.SchemaConstants.*; 054 import static org.opends.server.util.StaticUtils.*; 055 056 057 058 /** 059 * This class implements the telephone number attribute syntax, which is defined 060 * in RFC 2252. Note that this can have two modes of operation, depending on 061 * its configuration. Most of the time, it will be very lenient when deciding 062 * what to accept, and will allow anything but only pay attention to the digits. 063 * However, it can also be configured in a "strict" mode, in which case it will 064 * only accept values in the E.123 international telephone number format. 065 */ 066 public class TelephoneNumberSyntax 067 extends AttributeSyntax<TelephoneNumberAttributeSyntaxCfg> 068 implements ConfigurationChangeListener<TelephoneNumberAttributeSyntaxCfg> 069 { 070 // Indicates whether this matching rule should operate in strict mode. 071 private boolean strictMode; 072 073 // The default equality matching rule for this syntax. 074 private EqualityMatchingRule defaultEqualityMatchingRule; 075 076 // The default substring matching rule for this syntax. 077 private SubstringMatchingRule defaultSubstringMatchingRule; 078 079 // The current configuration for this telephone number syntax. 080 private TelephoneNumberAttributeSyntaxCfg currentConfig; 081 082 083 084 /** 085 * Creates a new instance of this syntax. Note that the only thing that 086 * should be done here is to invoke the default constructor for the 087 * superclass. All initialization should be performed in the 088 * <CODE>initializeSyntax</CODE> method. 089 */ 090 public TelephoneNumberSyntax() 091 { 092 super(); 093 } 094 095 096 097 /** 098 * {@inheritDoc} 099 */ 100 public void initializeSyntax(TelephoneNumberAttributeSyntaxCfg configuration) 101 throws ConfigException 102 { 103 defaultEqualityMatchingRule = 104 DirectoryServer.getEqualityMatchingRule(EMR_TELEPHONE_OID); 105 if (defaultEqualityMatchingRule == null) 106 { 107 logError(ERR_ATTR_SYNTAX_UNKNOWN_EQUALITY_MATCHING_RULE.get( 108 EMR_TELEPHONE_OID, SYNTAX_TELEPHONE_NAME)); 109 } 110 111 defaultSubstringMatchingRule = 112 DirectoryServer.getSubstringMatchingRule(SMR_TELEPHONE_OID); 113 if (defaultSubstringMatchingRule == null) 114 { 115 logError(ERR_ATTR_SYNTAX_UNKNOWN_SUBSTRING_MATCHING_RULE.get( 116 SMR_TELEPHONE_OID, SYNTAX_TELEPHONE_NAME)); 117 } 118 119 120 // We may or may not have access to the config entry. If we do, then see if 121 // we should use the strict compliance mode. If not, just assume that we 122 // won't. 123 strictMode = false; 124 if (configuration != null) 125 { 126 currentConfig = configuration; 127 currentConfig.addTelephoneNumberChangeListener(this); 128 strictMode = currentConfig.isStrictFormat(); 129 } 130 } 131 132 133 134 /** 135 * Performs any finalization that may be necessary for this attribute syntax. 136 */ 137 public void finalizeSyntax() 138 { 139 currentConfig.removeTelephoneNumberChangeListener(this); 140 } 141 142 143 144 /** 145 * Retrieves the common name for this attribute syntax. 146 * 147 * @return The common name for this attribute syntax. 148 */ 149 public String getSyntaxName() 150 { 151 return SYNTAX_TELEPHONE_NAME; 152 } 153 154 155 156 /** 157 * Retrieves the OID for this attribute syntax. 158 * 159 * @return The OID for this attribute syntax. 160 */ 161 public String getOID() 162 { 163 return SYNTAX_TELEPHONE_OID; 164 } 165 166 167 168 /** 169 * Retrieves a description for this attribute syntax. 170 * 171 * @return A description for this attribute syntax. 172 */ 173 public String getDescription() 174 { 175 return SYNTAX_TELEPHONE_DESCRIPTION; 176 } 177 178 179 180 /** 181 * Retrieves the default equality matching rule that will be used for 182 * attributes with this syntax. 183 * 184 * @return The default equality matching rule that will be used for 185 * attributes with this syntax, or <CODE>null</CODE> if equality 186 * matches will not be allowed for this type by default. 187 */ 188 public EqualityMatchingRule getEqualityMatchingRule() 189 { 190 return defaultEqualityMatchingRule; 191 } 192 193 194 195 /** 196 * Retrieves the default ordering matching rule that will be used for 197 * attributes with this syntax. 198 * 199 * @return The default ordering matching rule that will be used for 200 * attributes with this syntax, or <CODE>null</CODE> if ordering 201 * matches will not be allowed for this type by default. 202 */ 203 public OrderingMatchingRule getOrderingMatchingRule() 204 { 205 // There is no ordering matching rule by default. 206 return null; 207 } 208 209 210 211 /** 212 * Retrieves the default substring matching rule that will be used for 213 * attributes with this syntax. 214 * 215 * @return The default substring matching rule that will be used for 216 * attributes with this syntax, or <CODE>null</CODE> if substring 217 * matches will not be allowed for this type by default. 218 */ 219 public SubstringMatchingRule getSubstringMatchingRule() 220 { 221 return defaultSubstringMatchingRule; 222 } 223 224 225 226 /** 227 * Retrieves the default approximate matching rule that will be used for 228 * attributes with this syntax. 229 * 230 * @return The default approximate matching rule that will be used for 231 * attributes with this syntax, or <CODE>null</CODE> if approximate 232 * matches will not be allowed for this type by default. 233 */ 234 public ApproximateMatchingRule getApproximateMatchingRule() 235 { 236 // There is no approximate matching rule by default. 237 return null; 238 } 239 240 241 242 /** 243 * Indicates whether the provided value is acceptable for use in an attribute 244 * with this syntax. If it is not, then the reason may be appended to the 245 * provided buffer. 246 * 247 * @param value The value for which to make the determination. 248 * @param invalidReason The buffer to which the invalid reason should be 249 * appended. 250 * 251 * @return <CODE>true</CODE> if the provided value is acceptable for use with 252 * this syntax, or <CODE>false</CODE> if not. 253 */ 254 public boolean valueIsAcceptable(ByteString value, 255 MessageBuilder invalidReason) 256 { 257 // No matter what, the value can't be empty or null. 258 String valueStr; 259 if ((value == null) || 260 ((valueStr = value.stringValue().trim()).length() == 0)) 261 { 262 invalidReason.append(ERR_ATTR_SYNTAX_TELEPHONE_EMPTY.get()); 263 return false; 264 } 265 266 int length = valueStr.length(); 267 268 269 if (strictMode) 270 { 271 // If the value does not start with a plus sign, then that's not 272 // acceptable. 273 if (valueStr.charAt(0) != '+') 274 { 275 Message message = ERR_ATTR_SYNTAX_TELEPHONE_NO_PLUS.get(valueStr); 276 invalidReason.append(message); 277 return false; 278 } 279 280 281 // Iterate through the remaining characters in the value. There must be 282 // at least one digit, and it must contain only valid digits and separator 283 // characters. 284 boolean digitSeen = false; 285 for (int i=1; i < length; i++) 286 { 287 char c = valueStr.charAt(i); 288 if (isDigit(c)) 289 { 290 digitSeen = true; 291 } 292 else if (! isSeparator(c)) 293 { 294 Message message = ERR_ATTR_SYNTAX_TELEPHONE_ILLEGAL_CHAR.get( 295 valueStr, String.valueOf(c), i); 296 invalidReason.append(message); 297 return false; 298 } 299 } 300 301 if (! digitSeen) 302 { 303 Message message = ERR_ATTR_SYNTAX_TELEPHONE_NO_DIGITS.get(valueStr); 304 invalidReason.append(message); 305 return false; 306 } 307 308 309 // If we've gotten here, then we'll consider it acceptable. 310 return true; 311 } 312 else 313 { 314 // If we are not in strict mode, then all non-empty values containing at 315 // least one digit will be acceptable. 316 for (int i=0; i < length; i++) 317 { 318 if (isDigit(valueStr.charAt(i))) 319 { 320 return true; 321 } 322 } 323 324 // If we made it here, then we didn't find any digits. 325 Message message = ERR_ATTR_SYNTAX_TELEPHONE_NO_DIGITS.get(valueStr); 326 invalidReason.append(message); 327 return false; 328 } 329 } 330 331 332 333 /** 334 * Indicates whether the provided character is a valid separator for telephone 335 * number components when operating in strict mode. 336 * 337 * @param c The character for which to make the determination. 338 * 339 * @return <CODE>true</CODE> if the provided character is a valid separator, 340 * or <CODE>false</CODE> if it is not. 341 */ 342 private boolean isSeparator(char c) 343 { 344 switch (c) 345 { 346 case ' ': 347 case '-': 348 return true; 349 default: 350 return false; 351 } 352 } 353 354 355 356 /** 357 * {@inheritDoc} 358 */ 359 public boolean isConfigurationChangeAcceptable( 360 TelephoneNumberAttributeSyntaxCfg configuration, 361 List<Message> unacceptableReasons) 362 { 363 // The configuration will always be acceptable. 364 return true; 365 } 366 367 368 369 /** 370 * {@inheritDoc} 371 */ 372 public ConfigChangeResult applyConfigurationChange( 373 TelephoneNumberAttributeSyntaxCfg configuration) 374 { 375 currentConfig = configuration; 376 strictMode = configuration.isStrictFormat(); 377 378 return new ConfigChangeResult(ResultCode.SUCCESS, false); 379 } 380 } 381