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    
029    
030    
031    import java.util.List;
032    
033    import org.opends.server.admin.std.server.SubstringMatchingRuleCfg;
034    import org.opends.server.api.SubstringMatchingRule;
035    import org.opends.server.config.ConfigException;
036    import org.opends.server.protocols.asn1.ASN1OctetString;
037    import org.opends.server.types.ByteString;
038    import org.opends.server.types.DirectoryException;
039    import org.opends.server.types.InitializationException;
040    
041    import static org.opends.server.schema.SchemaConstants.*;
042    import static org.opends.server.util.StaticUtils.*;
043    
044    
045    
046    /**
047     * This class implements the telephoneNumberSubstringsMatch matching rule
048     * defined in X.520 and referenced in RFC 2252.  Note that although the
049     * specification calls for a very rigorous format, this is widely ignored so
050     * this matching will compare only numeric digits and strip out everything else.
051     */
052    public class TelephoneNumberSubstringMatchingRule
053           extends SubstringMatchingRule
054    {
055      /**
056       * Creates a new instance of this telephoneNumberSubstringsMatch matching
057       * rule.
058       */
059      public TelephoneNumberSubstringMatchingRule()
060      {
061        super();
062      }
063    
064    
065    
066      /**
067       * {@inheritDoc}
068       */
069      public void initializeMatchingRule(SubstringMatchingRuleCfg configuration)
070             throws ConfigException, InitializationException
071      {
072        // No initialization is required.
073      }
074    
075    
076    
077      /**
078       * Retrieves the common name for this matching rule.
079       *
080       * @return  The common name for this matching rule, or <CODE>null</CODE> if
081       * it does not have a name.
082       */
083      public String getName()
084      {
085        return SMR_TELEPHONE_NAME;
086      }
087    
088    
089    
090      /**
091       * Retrieves the OID for this matching rule.
092       *
093       * @return  The OID for this matching rule.
094       */
095      public String getOID()
096      {
097        return SMR_TELEPHONE_OID;
098      }
099    
100    
101    
102      /**
103       * Retrieves the description for this matching rule.
104       *
105       * @return  The description for this matching rule, or <CODE>null</CODE> if
106       *          there is none.
107       */
108      public String getDescription()
109      {
110        // There is no standard description for this matching rule.
111        return null;
112      }
113    
114    
115    
116      /**
117       * Retrieves the OID of the syntax with which this matching rule is
118       * associated.
119       *
120       * @return  The OID of the syntax with which this matching rule is associated.
121       */
122      public String getSyntaxOID()
123      {
124        return SYNTAX_SUBSTRING_ASSERTION_OID;
125      }
126    
127    
128    
129      /**
130       * Retrieves the normalized form of the provided value, which is best suited
131       * for efficiently performing matching operations on that value.
132       *
133       * @param  value  The value to be normalized.
134       *
135       * @return  The normalized version of the provided value.
136       *
137       * @throws  DirectoryException  If the provided value is invalid according to
138       *                              the associated attribute syntax.
139       */
140      public ByteString normalizeValue(ByteString value)
141             throws DirectoryException
142      {
143        String valueString = value.stringValue();
144        int    valueLength = valueString.length();
145        StringBuilder buffer = new StringBuilder(valueLength);
146    
147    
148        // Iterate through the characters in the value and filter out everything
149        // that isn't a digit.
150        for (int i=0; i < valueLength; i++)
151        {
152          char c = valueString.charAt(i);
153          if (isDigit(c))
154          {
155            buffer.append(c);
156          }
157        }
158    
159    
160        return new ASN1OctetString(buffer.toString());
161      }
162    
163    
164    
165      /**
166       * Normalizes the provided value fragment into a form that can be used to
167       * efficiently compare values.
168       *
169       * @param  substring  The value fragment to be normalized.
170       *
171       * @return  The normalized form of the value fragment.
172       *
173       * @throws  DirectoryException  If the provided value fragment is not
174       *                              acceptable according to the associated syntax.
175       */
176      public ByteString normalizeSubstring(ByteString substring)
177             throws DirectoryException
178      {
179        // In this case, the logic used to normalize a substring is identical to the
180        // logic used to normalize a full value.
181        return normalizeValue(substring);
182      }
183    
184    
185    
186      /**
187       * Determines whether the provided value matches the given substring filter
188       * components.  Note that any of the substring filter components may be
189       * <CODE>null</CODE> but at least one of them must be non-<CODE>null</CODE>.
190       *
191       * @param  value           The normalized value against which to compare the
192       *                         substring components.
193       * @param  subInitial      The normalized substring value fragment that should
194       *                         appear at the beginning of the target value.
195       * @param  subAnyElements  The normalized substring value fragments that
196       *                         should appear in the middle of the target value.
197       * @param  subFinal        The normalized substring value fragment that should
198       *                         appear at the end of the target value.
199       *
200       * @return  <CODE>true</CODE> if the provided value does match the given
201       *          substring components, or <CODE>false</CODE> if not.
202       */
203      public boolean valueMatchesSubstring(ByteString value, ByteString subInitial,
204                                           List<ByteString> subAnyElements,
205                                           ByteString subFinal)
206      {
207        byte[] valueBytes = value.value();
208        int valueLength = valueBytes.length;
209    
210        int pos = 0;
211        if (subInitial != null)
212        {
213          byte[] initialBytes = subInitial.value();
214          int initialLength = initialBytes.length;
215          if (initialLength > valueLength)
216          {
217            return false;
218          }
219    
220          for (; pos < initialLength; pos++)
221          {
222            if (initialBytes[pos] != valueBytes[pos])
223            {
224              return false;
225            }
226          }
227        }
228    
229    
230        if ((subAnyElements != null) && (! subAnyElements.isEmpty()))
231        {
232          for (ByteString element : subAnyElements)
233          {
234            byte[] anyBytes = element.value();
235            int anyLength = anyBytes.length;
236    
237            int end = valueLength - anyLength;
238            boolean match = false;
239            for (; pos <= end; pos++)
240            {
241              if (anyBytes[0] == valueBytes[pos])
242              {
243                boolean subMatch = true;
244                for (int i=1; i < anyLength; i++)
245                {
246                  if (anyBytes[i] != valueBytes[pos+i])
247                  {
248                    subMatch = false;
249                    break;
250                  }
251                }
252    
253                if (subMatch)
254                {
255                  match = subMatch;
256                  break;
257                }
258              }
259            }
260    
261            if (match)
262            {
263              pos += anyLength;
264            }
265            else
266            {
267              return false;
268            }
269          }
270        }
271    
272    
273        if (subFinal != null)
274        {
275          byte[] finalBytes = subFinal.value();
276          int finalLength = finalBytes.length;
277    
278          if ((valueLength - finalLength) < pos)
279          {
280            return false;
281          }
282    
283          pos = valueLength - finalLength;
284          for (int i=0; i < finalLength; i++,pos++)
285          {
286            if (finalBytes[i] != valueBytes[pos])
287            {
288              return false;
289            }
290          }
291        }
292    
293    
294        return true;
295      }
296    }
297