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 org.opends.server.admin.std.server.OrderingMatchingRuleCfg;
033    import org.opends.server.api.OrderingMatchingRule;
034    import org.opends.server.config.ConfigException;
035    import org.opends.server.core.DirectoryServer;
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    import org.opends.server.types.ResultCode;
041    
042    import static org.opends.server.loggers.ErrorLogger.*;
043    import static org.opends.messages.SchemaMessages.*;
044    import static org.opends.server.schema.SchemaConstants.*;
045    
046    
047    /**
048     * This class defines the integerOrderingMatch matching rule defined in X.520
049     * and referenced in RFC 4519.
050     */
051    public class IntegerOrderingMatchingRule
052           extends OrderingMatchingRule
053    {
054      /**
055       * The serial version identifier required to satisfy the compiler because this
056       * class implements the <CODE>java.io.Serializable</CODE> interface.  This
057       * value was generated using the <CODE>serialver</CODE> command-line utility
058       * included with the Java SDK.
059       */
060      private static final long serialVersionUID = 6654300545706161754L;
061    
062    
063    
064      /**
065       * Creates a new instance of this integerOrderingMatch matching rule.
066       */
067      public IntegerOrderingMatchingRule()
068      {
069        super();
070      }
071    
072    
073    
074      /**
075       * {@inheritDoc}
076       */
077      public void initializeMatchingRule(OrderingMatchingRuleCfg configuration)
078             throws ConfigException, InitializationException
079      {
080        // No initialization is required.
081      }
082    
083    
084    
085      /**
086       * Retrieves the common name for this matching rule.
087       *
088       * @return  The common name for this matching rule, or <CODE>null</CODE> if
089       * it does not have a name.
090       */
091      public String getName()
092      {
093        return OMR_INTEGER_NAME;
094      }
095    
096    
097    
098      /**
099       * Retrieves the OID for this matching rule.
100       *
101       * @return  The OID for this matching rule.
102       */
103      public String getOID()
104      {
105        return OMR_INTEGER_OID;
106      }
107    
108    
109    
110      /**
111       * Retrieves the description for this matching rule.
112       *
113       * @return  The description for this matching rule, or <CODE>null</CODE> if
114       *          there is none.
115       */
116      public String getDescription()
117      {
118        // There is no standard description for this matching rule.
119        return null;
120      }
121    
122    
123    
124      /**
125       * Retrieves the OID of the syntax with which this matching rule is
126       * associated.
127       *
128       * @return  The OID of the syntax with which this matching rule is associated.
129       */
130      public String getSyntaxOID()
131      {
132        return SYNTAX_INTEGER_OID;
133      }
134    
135    
136    
137      /**
138       * Retrieves the normalized form of the provided value, which is best suited
139       * for efficiently performing matching operations on that value.
140       *
141       * @param  value  The value to be normalized.
142       *
143       * @return  The normalized version of the provided value.
144       *
145       * @throws  DirectoryException  If the provided value is invalid according to
146       *                              the associated attribute syntax.
147       */
148      public ByteString normalizeValue(ByteString value)
149             throws DirectoryException
150      {
151        byte[] valueBytes = value.value();
152    
153        int length = valueBytes.length;
154        StringBuilder buffer = new StringBuilder(length);
155    
156        boolean logged = false;
157        for (int i=0; i < length; i++)
158        {
159          switch (valueBytes[i])
160          {
161            case '0':
162              switch (buffer.length())
163              {
164                case 0:
165                  // This is only OK if the value is zero
166                  if (i == (length-1))
167                  {
168                    buffer.append("0");
169                  }
170                  else
171                  {
172    
173                    Message message = WARN_ATTR_SYNTAX_INTEGER_INITIAL_ZERO.get(
174                            value.stringValue());
175    
176                    switch (DirectoryServer.getSyntaxEnforcementPolicy())
177                    {
178                      case REJECT:
179                        throw new DirectoryException(
180                                     ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
181                      case WARN:
182                        if (! logged)
183                        {
184                          logged = true;
185                          logError(message);
186                        }
187                        break;
188                    }
189                  }
190                  break;
191                case 1:
192                  // This is OK as long as the first character isn't a dash.
193                  if (buffer.charAt(0) == '-')
194                  {
195    
196                    Message message = WARN_ATTR_SYNTAX_INTEGER_INITIAL_ZERO.get(
197                            value.stringValue());
198    
199                    switch (DirectoryServer.getSyntaxEnforcementPolicy())
200                    {
201                      case REJECT:
202                        throw new DirectoryException(
203                                      ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
204                      case WARN:
205                        if (! logged)
206                        {
207                          logged = true;
208                          logError(
209                                  message);
210                        }
211                        break;
212                    }
213                  }
214                  else
215                  {
216                    buffer.append("0");
217                  }
218                  break;
219                default:
220                  // This is always fine.
221                  buffer.append("0");
222                  break;
223              }
224              break;
225            case '1':
226              buffer.append('1');
227              break;
228            case '2':
229              buffer.append('2');
230              break;
231            case '3':
232              buffer.append('3');
233              break;
234            case '4':
235              buffer.append('4');
236              break;
237            case '5':
238              buffer.append('5');
239              break;
240            case '6':
241              buffer.append('6');
242              break;
243            case '7':
244              buffer.append('7');
245              break;
246            case '8':
247              buffer.append('8');
248              break;
249            case '9':
250              buffer.append('9');
251              break;
252            case '-':
253              // This is only OK if the buffer is empty.
254              if (buffer.length() == 0)
255              {
256                buffer.append("-");
257              }
258              else
259              {
260                Message message = WARN_ATTR_SYNTAX_INTEGER_MISPLACED_DASH.get(
261                        value.stringValue());
262    
263                switch (DirectoryServer.getSyntaxEnforcementPolicy())
264                {
265                  case REJECT:
266                    throw new DirectoryException(
267                                   ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
268                  case WARN:
269                    if (! logged)
270                    {
271                      logged = true;
272                      logError(
273                              message);
274                    }
275                    break;
276                }
277              }
278              break;
279            default:
280              Message message = WARN_ATTR_SYNTAX_INTEGER_INVALID_CHARACTER.get(
281                      value.stringValue(),
282                      ((char) valueBytes[i]), i);
283              switch (DirectoryServer.getSyntaxEnforcementPolicy())
284              {
285                case REJECT:
286                  throw new DirectoryException(
287                                 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
288                case WARN:
289                  if (! logged)
290                  {
291                    logged = true;
292                    logError(
293                            message);
294                  }
295                  break;
296              }
297          }
298        }
299    
300        if (buffer.length() == 0)
301        {
302          Message message = WARN_ATTR_SYNTAX_INTEGER_EMPTY_VALUE.get(
303                  value.stringValue());
304    
305          switch (DirectoryServer.getSyntaxEnforcementPolicy())
306          {
307            case REJECT:
308              throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
309                                           message);
310    
311            case WARN:
312              if (! logged)
313              {
314                logged = true;
315                logError(
316                        message);
317              }
318    
319              buffer.append("0");
320              break;
321    
322            default:
323              buffer.append("0");
324              break;
325          }
326        }
327        else if ((buffer.length() == 1) && (buffer.charAt(0) == '-'))
328        {
329          Message message = WARN_ATTR_SYNTAX_INTEGER_DASH_NEEDS_VALUE.get(
330                  value.stringValue());
331    
332          switch (DirectoryServer.getSyntaxEnforcementPolicy())
333          {
334            case REJECT:
335              throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
336                                           message);
337    
338            case WARN:
339              if (! logged)
340              {
341                logged = true;
342                logError(
343                        message);
344              }
345    
346              buffer.setCharAt(0, '0');
347              break;
348    
349            default:
350              buffer.setCharAt(0, '0');
351              break;
352          }
353        }
354    
355        return new ASN1OctetString(buffer.toString());
356      }
357    
358    
359    
360      /**
361       * Compares the first value to the second and returns a value that indicates
362       * their relative order.
363       *
364       * @param  value1  The normalized form of the first value to compare.
365       * @param  value2  The normalized form of the second value to compare.
366       *
367       * @return  A negative integer if <CODE>value1</CODE> should come before
368       *          <CODE>value2</CODE> in ascending order, a positive integer if
369       *          <CODE>value1</CODE> should come after <CODE>value2</CODE> in
370       *          ascending order, or zero if there is no difference between the
371       *          values with regard to ordering.
372       */
373      public int compareValues(ByteString value1, ByteString value2)
374      {
375        return compare(value1.value(), value2.value());
376      }
377    
378    
379    
380      /**
381       * Compares the contents of the provided byte arrays to determine their
382       * relative order.
383       *
384       * @param  b1  The first byte array to use in the comparison.
385       * @param  b2  The second byte array to use in the comparison.
386       *
387       * @return  A negative integer if <CODE>b1</CODE> should come before
388       *          <CODE>b2</CODE> in ascending order, a positive integer if
389       *          <CODE>b1</CODE> should come after <CODE>b2</CODE> in ascending
390       *          order, or zero if there is no difference between the values with
391       *          regard to ordering.
392       */
393      public int compare(byte[] b1, byte[] b2)
394      {
395        int b1Length = b1.length;
396        int b2Length = b2.length;
397    
398    
399        // A length of zero should be considered a value of zero.
400        if (b1Length == 0)
401        {
402          if (b2Length == 0)
403          {
404            return 0;
405          }
406          else if (b2[0] == '-')
407          {
408            return 1;
409          }
410          else
411          {
412            return -1;
413          }
414        }
415        else if (b2Length == 0)
416        {
417          if (b1[0] == '-')
418          {
419            return -1;
420          }
421          else
422          {
423            return 1;
424          }
425        }
426    
427    
428        // Starting with a dash should be an indicator of a negative value.
429        if (b1[0] == '-')
430        {
431          if (b2[0] == '-')
432          {
433            if (b1Length > b2Length)
434            {
435              return -1;
436            }
437            else if (b2Length > b1Length)
438            {
439              return 1;
440            }
441            else
442            {
443              for (int i=1; i < b1Length; i++)
444              {
445                if (b1[i] > b2[i])
446                {
447                  return -1;
448                }
449                else if (b1[i] < b2[i])
450                {
451                  return 1;
452                }
453              }
454    
455              return 0;
456            }
457          }
458          else
459          {
460            return -1;
461          }
462        }
463        else if (b2[0] == '-')
464        {
465          return 1;
466        }
467    
468    
469        // They are both positive, so see which one's bigger.
470        if (b1Length > b2Length)
471        {
472          return 1;
473        }
474        else if (b2Length > b1Length)
475        {
476          return -1;
477        }
478        else
479        {
480          for (int i=0; i < b1Length; i++)
481          {
482            if (b1[i] > b2[i])
483            {
484              return 1;
485            }
486            else if (b1[i] < b2[i])
487            {
488              return -1;
489            }
490          }
491    
492          return 0;
493        }
494      }
495    }
496