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