From 1d2462bb4e2fe9c8c4a2fb9f9df0c7a5bc841339 Mon Sep 17 00:00:00 2001
From: sin <sin@localhost>
Date: Fri, 18 Sep 2009 23:05:16 +0000
Subject: [PATCH] Fix for issue 4226: PartialDateOrTimeMatchingRule doesn't do time matching

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/schema/TimeBasedMatchingRuleTest.java |   97 +++++++++--
 opends/src/messages/messages/schema.properties                                                    |   25 ++
 opends/src/server/org/opends/server/schema/TimeBasedMatchingRuleFactory.java                      |  324 ++++++++++++++++++++++-----------------
 3 files changed, 279 insertions(+), 167 deletions(-)

diff --git a/opends/src/messages/messages/schema.properties b/opends/src/messages/messages/schema.properties
index 798246a..2fff3d5 100644
--- a/opends/src/messages/messages/schema.properties
+++ b/opends/src/messages/messages/schema.properties
@@ -966,11 +966,11 @@
  ,h(hour),d(day) and w(week)
 MILD_WARN_ATTR_INVALID_PARTIAL_TIME_ASSERTION_FORMAT_295=The provided \
  value "%s" could not be parsed as a valid assertion value because the \
- character '%c' is not allowed. The acceptable values are DD (date),MM(month) \
- and YYYY(year)
-MILD_WARN_ATTR_MISSING_CHAR_PARTIAL_TIME_ASSERTION_FORMAT_296=The provided \
- value "%s" could not be parsed as a valid assertion value because an invalid \
- character '%c' is  found instead of '%c' at position %d
+ character '%c' is not allowed. The acceptable values are s(second), \
+ m (minute), h (hour), D (date), M(month) and Y(year)
+MILD_WARN_ATTR_INVALID_SECOND_ASSERTION_FORMAT_296=The provided \
+  value "%s" could not be parsed as a valid assertion value because "%d" is not \
+  a valid second specification
 MILD_WARN_ATTR_INVALID_DATE_ASSERTION_FORMAT_297=The provided \
   value "%s" could not be parsed as a valid assertion value because "%d" is not \
   a valid date specification
@@ -1015,3 +1015,18 @@
 MILD_WARN_ATTR_SYNTAX_LDAPSYNTAX_ENUM_DUPLICATE_VALUE_311=The provided value \
   "%s" cannot be parsed as an enumeration syntax  because it contains a \
   duplicate value "%s" at position %d
+MILD_WARN_ATTR_INVALID_MINUTE_ASSERTION_FORMAT_312=The provided \
+  value "%s" could not be parsed as a valid assertion value because "%d" is not \
+  a valid minute specification
+MILD_WARN_ATTR_INVALID_HOUR_ASSERTION_FORMAT_313=The provided \
+  value "%s" could not be parsed as a valid assertion value because "%d" is not \
+  a valid hour specification
+MILD_WARN_ATTR_DUPLICATE_SECOND_ASSERTION_FORMAT_314=The provided \
+  value "%s" could not be parsed as a valid assertion value because there is  \
+  conflicting  value "%d" for s(Second) specification
+MILD_WARN_ATTR_DUPLICATE_MINUTE_ASSERTION_FORMAT_315=The provided \
+  value "%s" could not be parsed as a valid assertion value because there is  \
+  conflicting  value "%d" for m(Minute) specification
+MILD_WARN_ATTR_DUPLICATE_HOUR_ASSERTION_FORMAT_316=The provided \
+  value "%s" could not be parsed as a valid assertion value because there is  \
+  conflicting  value "%d" for h(Hour) specification
\ No newline at end of file
diff --git a/opends/src/server/org/opends/server/schema/TimeBasedMatchingRuleFactory.java b/opends/src/server/org/opends/server/schema/TimeBasedMatchingRuleFactory.java
index 70d5486..489f64e 100644
--- a/opends/src/server/org/opends/server/schema/TimeBasedMatchingRuleFactory.java
+++ b/opends/src/server/org/opends/server/schema/TimeBasedMatchingRuleFactory.java
@@ -53,6 +53,7 @@
 import org.opends.server.types.AttributeValue;
 import org.opends.server.types.ByteSequence;
 import org.opends.server.types.ByteString;
+import org.opends.server.types.ByteStringBuilder;
 import org.opends.server.types.ConditionResult;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.IndexConfig;
@@ -95,42 +96,23 @@
       TimeZone.getTimeZone(TIME_ZONE_UTC);
 
 
-  //Constants for months.
-  private static final byte[] JAN = {'j','a','n' };
+  //Constants for generating keys.
+  private static final char SECOND = 's';
 
 
-  private static final byte[] FEB = {'f','e','b'};
+  private static final char MINUTE = 'm';
 
 
-  private static final byte[] MAR = {'m','a','r'};
+  private static final char HOUR = 'h';
 
 
-  private static final byte[] APR = {'a','p','r'};
+  private static final char MONTH = 'M';
 
 
-  private static final byte[] MAY = {'m','a','y'};
+  private static final char DATE = 'D';
 
 
-  private static final byte[] JUN = {'j','u','n'};
-
-
-  private static final byte[] JUL = {'j','u','l'};
-
-
-  private static final byte[] AUG = {'a','u','g'};
-
-
-  private static final byte[] SEP = {'s','e','p'};
-
-
-  private static final byte[] OCT = {'o','c','t'};
-
-
-  private static final byte[] NOV = {'n','o','v'};
-
-
-  private static final byte[] DEC = {'d','e','c'};
-
+  private static final char YEAR = 'Y';
 
 
   /**
@@ -793,17 +775,23 @@
     {
      /**
       An assertion value may contain one or all of the following:
-      DD = day
-      MM = month
-      YYYY = year
+      D = day
+      M = month
+      Y = year
+      h = hour
+      m = month
+      s = second
 
-      An example assertion is OID:=04MM. In this example we are
+      An example assertion is OID:=04M. In this example we are
       searching for entries corresponding to month of april.
 
       Use this method to parse, validate and normalize the assertion value
       into a format to be recognized by the compare routine. The normalized
-      value is actually the format of : DDMMYYYY.
+      value is actually the format of : smhDMY.
       */
+      int second = -1;
+      int minute = -1;
+      int hour = -1;
       int date = 0;
       int year = 0;
       int number = 0;
@@ -863,16 +851,44 @@
           Message message = null;
           switch(value.byteAt(index))
           {
-            case 'D':
-              if(!(index < length-1) || value.byteAt(index+1) !='D')
+            case 's':
+              if(second >0)
               {
-                //the acceptable format is 'DD'.
-                message =
-                        WARN_ATTR_MISSING_CHAR_PARTIAL_TIME_ASSERTION_FORMAT.
-                        get(value.toString(),
-                        (char)value.byteAt(index),'D',index+1);
+                 message =
+                        WARN_ATTR_DUPLICATE_SECOND_ASSERTION_FORMAT.get(
+                        value.toString(),date);
               }
-              else if(number == 0)
+              else
+              {
+                second = number;
+              }
+              break;
+            case 'm':
+              if(minute >0)
+              {
+                 message =
+                        WARN_ATTR_DUPLICATE_MINUTE_ASSERTION_FORMAT.get(
+                        value.toString(),date);
+              }
+              else
+              {
+                minute = number;
+              }
+              break;
+            case 'h':
+              if(hour >0)
+              {
+                 message =
+                        WARN_ATTR_DUPLICATE_HOUR_ASSERTION_FORMAT.get(
+                        value.toString(),date);
+              }
+              else
+              {
+                hour = number;
+              }
+              break;
+            case 'D':
+              if(number == 0)
               {
                 message =
                         WARN_ATTR_INVALID_DATE_ASSERTION_FORMAT.get(
@@ -887,19 +903,10 @@
               else
               {
                 date = number;
-                index++;
               }
               break;
             case 'M':
-              if(!(index < length-1) || value.byteAt(index+1)!='M')
-              {
-                //the acceptable value is 'MM'.
-                message =
-                        WARN_ATTR_MISSING_CHAR_PARTIAL_TIME_ASSERTION_FORMAT.
-                        get(value.toString(),
-                        (char)value.byteAt(index),'M',index+1);
-              }
-              else if(number == 0)
+               if(number == 0)
               {
                 message =
                         WARN_ATTR_INVALID_MONTH_ASSERTION_FORMAT.
@@ -914,48 +921,23 @@
               else
               {
                 month = number;
-                index++;
               }
               break;
             case 'Y':
-              if(!(index < length-3))
+              if(number == 0)
               {
-                //the acceptable value is 'YYYY".
                 message =
-                        WARN_ATTR_MISSING_YEAR_PARTIAL_TIME_ASSERTION_FORMAT.
-                        get(value.toString());
+                        WARN_ATTR_INVALID_YEAR_ASSERTION_FORMAT.
+                        get(value.toString(),number);
+              }
+              else if(year >0)
+              {
+                message = WARN_ATTR_DUPLICATE_YEAR_ASSERTION_FORMAT.
+                        get(value.toString(),year);
               }
               else
               {
-yearLoop: for(int i=index;i<index+3;i++)
-                {
-                  if(value.byteAt(i) !='Y')
-                  {
-                    message =
-                         WARN_ATTR_MISSING_CHAR_PARTIAL_TIME_ASSERTION_FORMAT.
-                        get(value.toString(),(char)value.byteAt(i),'Y',i);
-                    break yearLoop;
-                  }
-                }
-                if(message == null)
-                {
-                  if(number == 0)
-                  {
-                    message =
-                            WARN_ATTR_INVALID_YEAR_ASSERTION_FORMAT.
-                            get(value.toString(),number);
-                  }
-                  else if(year >0)
-                  {
-                    message = WARN_ATTR_DUPLICATE_YEAR_ASSERTION_FORMAT.
-                            get(value.toString(),year);
-                  }
-                  else
-                  {
-                    year = number;
-                    index+=3;
-                  }
-                }
+                year = number;
               }
               break;
             default:
@@ -976,7 +958,7 @@
         }
       }
 
-      //Validate year, month and date in that order.
+      //Validate year, month , date , hour, minute and second in that order.
       if(year < 0)
       {
         //A future date is allowed.
@@ -1072,11 +1054,44 @@
                 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
       }
 
+      if(!(hour >=-1 && hour <=23))
+      {
+         Message message =
+                WARN_ATTR_INVALID_HOUR_ASSERTION_FORMAT.
+                get(value.toString(),date);
+        logError(message);
+        throw new DirectoryException(
+                ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
+      }
+
+      if(!(minute >=-1 && minute <=59))
+      {
+           Message message =
+                WARN_ATTR_INVALID_MINUTE_ASSERTION_FORMAT.
+                get(value.toString(),date);
+        logError(message);
+        throw new DirectoryException(
+                ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
+      }
+
+      if(!(second >=-1 && second <=60)) //Consider leap seconds.
+      {
+         Message message =
+                WARN_ATTR_INVALID_SECOND_ASSERTION_FORMAT.
+                get(value.toString(),date);
+        logError(message);
+        throw new DirectoryException(
+                ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
+      }
+
       /**
        * Since we reached here we have a valid assertion value. Construct
-       * a normalized value in the order: DATE MONTH YEAR.
+       * a normalized value in the order: SECOND MINUTE HOUR  DATE MONTH YEAR.
        */
-      ByteBuffer bb = ByteBuffer.allocate(3*4);
+      ByteBuffer bb = ByteBuffer.allocate(6*4);
+      bb.putInt(second);
+      bb.putInt(minute);
+      bb.putInt(hour);
       bb.putInt(date);
       bb.putInt(month);
       bb.putInt(year);
@@ -1097,15 +1112,37 @@
       GregorianCalendar cal = new GregorianCalendar(TIME_ZONE_UTC_OBJ);
       cal.setLenient(false);
       cal.setTimeInMillis(timeInMS);
+      int second = cal.get(Calendar.SECOND);
+      int minute = cal.get(Calendar.MINUTE);
+      int hour = cal.get(Calendar.HOUR_OF_DAY);
       int date = cal.get(Calendar.DATE);
       int month = cal.get(Calendar.MONTH);
       int year = cal.get(Calendar.YEAR);
 
+
       //Build the information from the assertion value.
       ByteBuffer bb = ByteBuffer.wrap(assertionValue.toByteArray());
-      int assertDate = bb.getInt(0);
-      int assertMonth = bb.getInt(4);
-      int assertYear = bb.getInt(8);
+      int assertSecond = bb.getInt(0);
+      int assertMinute = bb.getInt(4);
+      int assertHour = bb.getInt(8);
+      int assertDate = bb.getInt(12);
+      int assertMonth = bb.getInt(16);
+      int assertYear = bb.getInt(20);
+
+      if(assertSecond != -1 && assertSecond !=second)
+      {
+        return ConditionResult.FALSE;
+      }
+
+      if(assertMinute !=-1 && assertMinute !=minute)
+      {
+        return ConditionResult.FALSE;
+      }
+
+      if(assertHour !=-1 && assertHour !=hour)
+      {
+        return ConditionResult.FALSE;
+      }
 
       //All the non-zero values should match.
       if(assertDate !=0 && assertDate != date)
@@ -1152,30 +1189,54 @@
       byte[] arr = normalizeAssertionValue(assertionValue).toByteArray();
       ByteBuffer bb = ByteBuffer.wrap(arr);
 
-      int assertDate = bb.getInt(0);
-      int assertMonth = bb.getInt(4);
-      int assertYear = bb.getInt(8);
+      int assertSecond = bb.getInt(0);
+      int assertMinute = bb.getInt(4);
+      int assertHour = bb.getInt(8);
+      int assertDate = bb.getInt(12);
+      int assertMonth = bb.getInt(16);
+      int assertYear = bb.getInt(20);
       List<T> queries = new ArrayList<T>();
 
+      if(assertSecond >= 0)
+      {
+        queries.add(factory.createExactMatchQuery(
+                indexer.getExtensibleIndexID(),
+               getKey(assertSecond,SECOND)));
+      }
+
+      if(assertMinute >=0)
+      {
+         queries.add(factory.createExactMatchQuery(
+                indexer.getExtensibleIndexID(),
+               getKey(assertMinute,MINUTE)));
+      }
+
+      if(assertHour >=0)
+      {
+         queries.add(factory.createExactMatchQuery(
+                indexer.getExtensibleIndexID(),
+               getKey(assertHour,HOUR)));
+      }
+
       if(assertDate >0)
       {
         queries.add(factory.createExactMatchQuery(
                 indexer.getExtensibleIndexID(),
-                ByteString.valueOf(assertDate)));
+                getKey(assertDate,DATE)));
       }
 
       if(assertMonth >=0)
       {
         queries.add(factory.createExactMatchQuery(
                 indexer.getExtensibleIndexID(),
-                ByteString.wrap(getMonthKey(assertMonth))));
+                getKey(assertMonth,MONTH)));
       }
 
       if(assertYear > 0)
       {
         queries.add(factory.createExactMatchQuery(
                 indexer.getExtensibleIndexID(),
-                ByteString.valueOf(assertYear)));
+                getKey(assertYear,YEAR)));
       }
       return factory.createIntersectionQuery(queries);
     }
@@ -1207,76 +1268,53 @@
       //Build the information from the attribute value.
       GregorianCalendar cal = new GregorianCalendar(TIME_ZONE_UTC_OBJ);
       cal.setTimeInMillis(timeInMS);
+      int second = cal.get(Calendar.SECOND);
+      int minute = cal.get(Calendar.MINUTE);
+      int hour = cal.get(Calendar.HOUR_OF_DAY);
       int date = cal.get(Calendar.DATE);
       int month = cal.get(Calendar.MONTH);
       int year = cal.get(Calendar.YEAR);
 
+      if (second >=0)
+      {
+        keys.add(getKey(second,SECOND).toByteArray());
+      }
+
+      if(minute >=0)
+      {
+        keys.add(getKey(minute,MINUTE).toByteArray());
+      }
+
+      if(hour >=0)
+      {
+        keys.add(getKey(hour,HOUR).toByteArray());
+      }
       //Insert date.
       if(date > 0)
       {
-        keys.add(ByteString.valueOf(date).toByteArray());
+        keys.add(getKey(date,DATE).toByteArray());
       }
 
       //Insert month.
       if(month >=0)
       {
-        keys.add(getMonthKey(month));
+        keys.add(getKey(month,MONTH).toByteArray());
       }
 
       if(year > 0)
       {
-        keys.add(ByteString.valueOf(year).toByteArray());
+        keys.add(getKey(year,YEAR).toByteArray());
       }
     }
 
 
 
-    //Returns a byte array of for the corresponding month.
-    private byte[] getMonthKey(int month)
+    private ByteString getKey(int value, char type)
     {
-      byte[] key = null;
-      switch(month)
-      {
-        case Calendar.JANUARY:
-          key = JAN;
-          break;
-        case Calendar.FEBRUARY:
-          key = FEB;
-          break;
-        case Calendar.MARCH:
-          key = MAR;
-          break;
-        case Calendar.APRIL:
-          key = APR;
-          break;
-        case Calendar.MAY:
-          key = MAY;
-          break;
-        case Calendar.JUNE:
-          key = JUN;
-          break;
-        case Calendar.JULY:
-          key = JUL;
-          break;
-        case Calendar.AUGUST:
-          key = AUG;
-          break;
-        case Calendar.SEPTEMBER:
-          key = SEP;
-          break;
-        case Calendar.OCTOBER:
-          key = OCT;
-          break;
-        case Calendar.NOVEMBER:
-          key = NOV;
-          break;
-        case Calendar.DECEMBER:
-          key = DEC;
-          break;
-        default:
-          key = new byte[0];
-       }
-      return key;
+      ByteStringBuilder builder = new ByteStringBuilder();
+      builder.append(type);
+      builder.append(value);
+      return builder.toByteString();
     }
   }
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/TimeBasedMatchingRuleTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/TimeBasedMatchingRuleTest.java
index 633cafb..e2e8812 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/TimeBasedMatchingRuleTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/TimeBasedMatchingRuleTest.java
@@ -42,6 +42,7 @@
 import org.opends.server.protocols.internal.InternalSearchOperation;
 import org.opends.server.protocols.ldap.LDAPFilter;
 import org.opends.server.types.ByteString;
+import org.opends.server.types.ConditionResult;
 import org.opends.server.types.DN;
 import org.opends.server.types.DereferencePolicy;
 import org.opends.server.types.DirectoryException;
@@ -285,7 +286,7 @@
    * Test to search using the partial date and time matching rule for an assertion value.
    */
   @Test()
-  public void testPartialDateNTimeMatchingRule() throws Exception
+  public void testPartialDateNTimeMatchingRuleUsingSearch() throws Exception
   {
     try
     {
@@ -293,7 +294,7 @@
       InternalClientConnection conn =
            InternalClientConnection.getRootConnection();
       int month = cal.get(Calendar.MONTH)+1; //month starts from 0 in the Calendar.
-      String assertion = cal.get(Calendar.DATE)+"DD"+month+"MM";
+      String assertion = cal.get(Calendar.DATE)+"D"+month+"M";
 
       InternalSearchOperation searchOperation =
            new InternalSearchOperation(
@@ -322,6 +323,21 @@
   }
 
 
+  /**
+   * Test to match the attribute and the assertion values using a partial date and time
+   * matching rule.
+   */
+  @Test(dataProvider="partialDateTimeValues")
+  public void testPartialDateNTimeMatch(long attributeValue,String assertionValue) throws Exception
+  {
+    MatchingRule partialTimeRule = DirectoryServer.getMatchingRule(
+            EXT_PARTIAL_DATE_TIME_NAME.toLowerCase());
+    ByteString str = partialTimeRule.normalizeAssertionValue(ByteString.valueOf(assertionValue));
+    assertTrue(partialTimeRule.valuesMatch(ByteString.valueOf(attributeValue), str) ==
+            ConditionResult.TRUE);
+  }
+
+
 
   /**
    * Tests the assertion syntax of the relative time matching rules.
@@ -355,7 +371,7 @@
   /**
    * Tests the assertion syntax of the partial date and time matching rules.
    */
-  @Test(dataProvider= "partialDateTimeValues")
+  @Test(dataProvider= "partialDateTimeSyntaxes")
   public void testPartialDateTimeMatchingRuleAssertionSyntax(String assertion,boolean isValid)
   {
     MatchingRule partialDTRule =
@@ -403,14 +419,45 @@
   }
 
 
-
   /**
-   * Generates data for testing partial date and time assertion syntax.
+   * Generates the data for testing partial time date and time values.
    */
   @DataProvider(name="partialDateTimeValues")
   private Object[][] createPartialDateTimeValues()
   {
+    GregorianCalendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+    c.setLenient(false);
+    c.clear();
+    c.set(Calendar.HOUR_OF_DAY,23);
+    c.set(Calendar.MINUTE,0);
+    c.set(Calendar.SECOND,0);
+    long time1 = c.getTimeInMillis();
+    c.set(Calendar.HOUR_OF_DAY,00);
+    c.set(Calendar.MINUTE,59);
+    c.set(Calendar.SECOND,59);
+    long time2 = c.getTimeInMillis();
+
+    return new Object[][] {
+      {time1,"0s"},
+      {time1,"0m"},
+      {time1,"23h"},
+      {time2,"59m59s"},
+      {time2,"0h59m59s"}
+    };
+  }
+
+
+
+  /**
+   * Generates data for testing partial date and time assertion syntax.
+   */
+  @DataProvider(name="partialDateTimeSyntaxes")
+  private Object[][] createPartialDateTimeSyntaxes()
+  {
     //Get the date today.
+    int second = cal.get(Calendar.SECOND);
+    int minute = cal.get(Calendar.MINUTE);
+    int hour = cal.get(Calendar.HOUR);
     int date = cal.get(Calendar.DATE);
     int month = cal.get(Calendar.MONTH) + 1;
     int year = cal.get(Calendar.YEAR);
@@ -419,24 +466,36 @@
       {"20MM30DD1978YY",false},
       {"02MM29DD2009YY",false},
       {"02MM31DD2010YY",false},
-      {"02MM29DD2008YYYY",true},
+      {"-1s",false},
+      {"02M29D2008Y",true},
       {"DDYY",false},
-      {"02DD",true},
-      {"12MM",true},
-      {"1978YYYY",true},
+      {"02D",true},
+      {"12M",true},
+      {"1978Y",true},
       {"0MM",false},
       {"20MM03DD10MM",false},
-      {date+"DD",true},
-      {month+"MM",true},
-      {year+"YYYY",true},
-      {month+"MM"+date+"DD",true},
-      {year+"YYYY"+date+"DD",true},
-      {month+"MM"+year+"YYYY"+date+"DD",true}      
+      {"00s12m13h",true},
+      {"00s12m14h1M3D1978Y",true},
+      {"1s",true},
+      {"12m",true},
+      {"23h",true},
+      {"61s",false},
+      {"60m",false},
+      {"24h",false},
+      {second+"s",true},
+      {minute+"m",true},
+      {hour+"h",true},
+      {date+"D",true},
+      {month+"M",true},
+      {year+"Y",true},
+      {month+"M"+date+"D",true},
+      {year+"Y"+date+"D",true},
+      {month+"M"+year+"Y"+date+"D",true}      
     };
   }
 
 
-
+  
 //validate if the args are found in the entries list.
   private boolean dnFoundInEntryList( List<SearchResultEntry> entries,DN ... dns)
   {
@@ -500,9 +559,9 @@
       "dn: cn=user5,dc=example,dc=com",
       "objectclass: person",
       "objectclass: testoc",
-      "cn: user4",
-      "sn: user4",
+      "cn: user5",
+      "sn: user5",
       "test-time-attribute: " + format(currentTime) // now.
     );
   }
-}
+  }

--
Gitblit v1.10.0