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