From 3e15deec52ab7eb4bdd76b51c1c496935987d656 Mon Sep 17 00:00:00 2001
From: sin <sin@localhost>
Date: Wed, 15 Jul 2009 15:34:06 +0000
Subject: [PATCH] Issue 4116 :Provide implementation for regex syntax

---
 opends/src/server/org/opends/server/types/Schema.java                                  |   43 ++
 opends/src/server/org/opends/server/core/SchemaConfigManager.java                      |  287 ++++++++--------
 opends/src/server/org/opends/server/schema/LDAPSyntaxDescriptionSyntax.java            |  327 +++++++++++++++---
 opends/src/messages/messages/schema.properties                                         |    9 
 opends/tests/unit-tests-testng/src/server/org/opends/server/schema/LDAPSyntaxTest.java |  287 ++++++++++++----
 opends/src/server/org/opends/server/backends/SchemaBackend.java                        |   60 +-
 6 files changed, 707 insertions(+), 306 deletions(-)

diff --git a/opends/src/messages/messages/schema.properties b/opends/src/messages/messages/schema.properties
index 35e5641..3e18ae2 100644
--- a/opends/src/messages/messages/schema.properties
+++ b/opends/src/messages/messages/schema.properties
@@ -1001,4 +1001,11 @@
 MILD_WARN_ATTR_SYNTAX_LDAPSYNTAX_UNKNOWN_EXT_306=The provided value "%s" \
  could not be parsed as an ldap syntax because it contains an unrecognized \
  extension %s at position %d
-
+MILD_WARN_ATTR_SYNTAX_LDAPSYNTAX_REGEX_INVALID_VALUE_307=The provided value \
+  "%s" cannot be parsed as a valid regex syntax because it does not match  \
+  the pattern "%s"
+MILD_WARN_ATTR_SYNTAX_LDAPSYNTAX_REGEX_NO_PATTERN_308=The provided value "%s" \
+ could not be parsed as a regex syntax because it does not contain a regex pattern
+MILD_WARN_ATTR_SYNTAX_LDAPSYNTAX_REGEX_INVALID_PATTERN_309=The provided value \
+  "%s" could not be parsed as a regex syntax because the provided regex \
+ pattern "%s" is invalid
diff --git a/opends/src/server/org/opends/server/backends/SchemaBackend.java b/opends/src/server/org/opends/server/backends/SchemaBackend.java
index a69218f..9912785 100644
--- a/opends/src/server/org/opends/server/backends/SchemaBackend.java
+++ b/opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -397,8 +397,9 @@
       LinkedHashSet<String> newDCRs = new LinkedHashSet<String>();
       LinkedHashSet<String> newDSRs = new LinkedHashSet<String>();
       LinkedHashSet<String> newMRUs = new LinkedHashSet<String>();
+      LinkedHashSet<String> newLSDs = new LinkedHashSet<String>();
       Schema.genConcatenatedSchema(newATs, newOCs, newNFs, newDCRs, newDSRs,
-                                   newMRUs);
+                                   newMRUs,newLSDs);
 
       // Next, generate lists of elements from the previous concatenated schema.
       // If there isn't a previous concatenated schema, then use the base
@@ -449,8 +450,9 @@
       LinkedHashSet<String> oldDCRs = new LinkedHashSet<String>();
       LinkedHashSet<String> oldDSRs = new LinkedHashSet<String>();
       LinkedHashSet<String> oldMRUs = new LinkedHashSet<String>();
+      LinkedHashSet<String> oldLSDs = new LinkedHashSet<String>();
       Schema.readConcatenatedSchema(concatFilePath, oldATs, oldOCs, oldNFs,
-                                    oldDCRs, oldDSRs, oldMRUs);
+                                    oldDCRs, oldDSRs, oldMRUs,oldLSDs);
 
       // Create a list of modifications and add any differences between the old
       // and new schema into them.
@@ -465,6 +467,8 @@
                                        mods);
       Schema.compareConcatenatedSchema(oldMRUs, newMRUs, matchingRuleUsesType,
                                        mods);
+      Schema.compareConcatenatedSchema(oldLSDs, newLSDs, ldapSyntaxesType,
+                                      mods);
       if (! mods.isEmpty())
       {
         DirectoryServer.setOfflineSchemaChanges(mods);
@@ -3472,12 +3476,36 @@
     // Start with an empty schema entry.
     Entry schemaEntry = createEmptySchemaEntry();
 
+     /**
+     * Add all of the ldap syntax descriptions to the schema entry. We do
+     * this only for the real part of the ldapsyntaxes attribute. The real part
+     * is read and write to/from the schema files.
+     */
+    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+    for (LDAPSyntaxDescription ldapSyntax :
+                                   schema.getLdapSyntaxDescriptions().values())
+    {
+      if(schemaFile.equals(ldapSyntax.getSchemaFile()))
+      {
+        values.add(AttributeValues.create(ldapSyntaxesType,
+                ldapSyntax.getDefinition()));
+      }
+    }
+
+   if (! values.isEmpty())
+   {
+     ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+     AttributeBuilder builder = new AttributeBuilder(ldapSyntaxesType);
+     builder.addAll(values);
+     attrList.add(builder.toAttribute());
+     schemaEntry.putAttribute(ldapSyntaxesType, attrList);
+   }
 
     // Add all of the appropriate attribute types to the schema entry.  We need
     // to be careful of the ordering to ensure that any superior types in the
     // same file are written before the subordinate types.
     HashSet<AttributeType> addedTypes = new HashSet<AttributeType>();
-    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+    values = new LinkedHashSet<AttributeValue>();
     for (AttributeType at : schema.getAttributeTypes().values())
     {
       if (schemaFile.equals(at.getSchemaFile()))
@@ -3616,32 +3644,6 @@
     }
 
 
-    /**
-     * Add all of the ldap syntax descriptions to the schema entry. We do
-     * this only for the real part of the ldapsyntaxes attribute. The real part
-     * is read and write to/from the schema files.
-     */
-    values = new LinkedHashSet<AttributeValue>();
-    for (LDAPSyntaxDescription ldapSyntax :
-                                   schema.getLdapSyntaxDescriptions().values())
-    {
-      if(schemaFile.equals(ldapSyntax.getSchemaFile()))
-      {
-        values.add(AttributeValues.create(ldapSyntaxesType,
-                ldapSyntax.getDefinition()));
-      }
-    }
-
-   if (! values.isEmpty())
-   {
-     ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
-     AttributeBuilder builder = new AttributeBuilder(ldapSyntaxesType);
-     builder.addAll(values);
-     attrList.add(builder.toAttribute());
-     schemaEntry.putAttribute(attributeTypesType, attrList);
-   }
-
-
     if (schemaFile.equals(FILE_USER_SCHEMA_ELEMENTS))
     {
       Map<String, Attribute> attributes = schema.getExtraAttributes();
diff --git a/opends/src/server/org/opends/server/core/SchemaConfigManager.java b/opends/src/server/org/opends/server/core/SchemaConfigManager.java
index 7aec8fb..186a1bd 100644
--- a/opends/src/server/org/opends/server/core/SchemaConfigManager.java
+++ b/opends/src/server/org/opends/server/core/SchemaConfigManager.java
@@ -526,6 +526,47 @@
 
     // Get the attributeTypes attribute from the entry.
     LinkedList<Modification> mods = new LinkedList<Modification>();
+    //parse the syntaxes first because attributes rely on these.
+    LDAPSyntaxDescriptionSyntax ldapSyntax;
+    try
+    {
+      ldapSyntax = (LDAPSyntaxDescriptionSyntax) schema.getSyntax(
+              SYNTAX_LDAP_SYNTAX_OID);
+      if (ldapSyntax == null)
+      {
+        ldapSyntax = new LDAPSyntaxDescriptionSyntax();
+        ldapSyntax.initializeSyntax(null);
+      }
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      ldapSyntax = new LDAPSyntaxDescriptionSyntax();
+      ldapSyntax.initializeSyntax(null);
+    }
+
+    AttributeType ldapSyntaxAttrType =
+         schema.getAttributeType(ATTR_LDAP_SYNTAXES_LC);
+    if (ldapSyntaxAttrType == null)
+    {
+      ldapSyntaxAttrType =
+           DirectoryServer.getDefaultAttributeType(ATTR_LDAP_SYNTAXES,
+                                                   ldapSyntax);
+    }
+
+    List<Attribute> ldapSyntaxList = entry.getAttribute(ldapSyntaxAttrType);
+    if ((ldapSyntaxList != null) && (! ldapSyntaxList.isEmpty()))
+    {
+      for (Attribute a : ldapSyntaxList)
+      {
+        mods.add(new Modification(ModificationType.ADD, a));
+      }
+    }
+
     AttributeTypeSyntax attrTypeSyntax;
     try
     {
@@ -773,47 +814,6 @@
       }
     }
 
-    // Get the ldapsyntaxes attribute from the entry.
-    LDAPSyntaxDescriptionSyntax ldapSyntax;
-    try
-    {
-      ldapSyntax = (LDAPSyntaxDescriptionSyntax) schema.getSyntax(
-              SYNTAX_LDAP_SYNTAX_OID);
-      if (ldapSyntax == null)
-      {
-        ldapSyntax = new LDAPSyntaxDescriptionSyntax();
-        ldapSyntax.initializeSyntax(null);
-      }
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      ldapSyntax = new LDAPSyntaxDescriptionSyntax();
-      ldapSyntax.initializeSyntax(null);
-    }
-
-    AttributeType ldapSyntaxAttrType =
-         schema.getAttributeType(ATTR_LDAP_SYNTAXES_LC);
-    if (ldapSyntaxAttrType == null)
-    {
-      ldapSyntaxAttrType =
-           DirectoryServer.getDefaultAttributeType(ATTR_LDAP_SYNTAXES,
-                                                   ldapSyntax);
-    }
-
-    List<Attribute> ldapSyntaxList = entry.getAttribute(ldapSyntaxAttrType);
-    if ((ldapSyntaxList != null) && (! ldapSyntaxList.isEmpty()))
-    {
-      for (Attribute a : ldapSyntaxList)
-      {
-        mods.add(new Modification(ModificationType.ADD, a));
-      }
-    }
-
     // Loop on all the attribute of the schema entry to
     // find the extra attribute that shoule be loaded in the Schema.
     for (Attribute attribute : entry.getAttributes())
@@ -824,6 +824,104 @@
       }
     }
 
+
+
+    // Parse the ldapsyntaxes definitions if there are any.
+    if (ldapSyntaxList != null)
+    {
+      for (Attribute a : ldapSyntaxList)
+      {
+        for (AttributeValue v : a)
+        {
+          LDAPSyntaxDescription syntaxDescription = null;
+          try
+          {
+            syntaxDescription = LDAPSyntaxDescriptionSyntax.decodeLDAPSyntax(
+                    v.getValue(),schema,false);
+            syntaxDescription.setExtraProperty(
+                    SCHEMA_PROPERTY_FILENAME, (String) null);
+            syntaxDescription.setSchemaFile(schemaFile);
+          }
+          catch (DirectoryException de)
+          {
+            if (debugEnabled())
+            {
+              TRACER.debugCaught(DebugLogLevel.ERROR, de);
+            }
+
+            Message message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_LDAP_SYNTAX.get(
+                    schemaFile,
+                    de.getMessageObject());
+
+            if (failOnError)
+            {
+              throw new ConfigException(message, de);
+            }
+            else
+            {
+              logError(message);
+              continue;
+            }
+          }
+          catch (Exception e)
+          {
+            if (debugEnabled())
+            {
+              TRACER.debugCaught(DebugLogLevel.ERROR, e);
+            }
+
+            Message message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_LDAP_SYNTAX.get(
+                    schemaFile,
+                    v.getValue().toString() + ":  " + getExceptionMessage(e));
+
+            if (failOnError)
+            {
+              throw new ConfigException(message, e);
+            }
+            else
+            {
+              logError(message);
+              continue;
+            }
+          }
+
+           // Register it with the schema.  We will allow duplicates, with the
+          // later definition overriding any earlier definition, but we want
+          // to trap them and log a warning.
+          try
+          {
+            schema.registerLdapSyntaxDescription(
+                                  syntaxDescription, failOnError);
+          }
+          catch (DirectoryException de)
+          {
+            if (debugEnabled())
+            {
+              TRACER.debugCaught(DebugLogLevel.ERROR, de);
+            }
+
+            Message message = WARN_CONFIG_SCHEMA_CONFLICTING_LDAP_SYNTAX.get(
+                schemaFile, de.getMessageObject());
+            logError(message);
+
+            try
+            {
+              schema.registerLdapSyntaxDescription(syntaxDescription, true);
+            }
+            catch (Exception e)
+            {
+              // This should never happen.
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, e);
+              }
+            }
+          }
+
+        }
+      }
+    }
+
     // Parse the attribute type definitions if there are any.
     if (attrList != null)
     {
@@ -1386,101 +1484,6 @@
       }
     }
 
-
-    // Parse the ldapsyntaxes definitions if there are any.
-    if (ldapSyntaxList != null)
-    {
-      for (Attribute a : ldapSyntaxList)
-      {
-        for (AttributeValue v : a)
-        {
-          LDAPSyntaxDescription syntaxDescription = null;
-          try
-          {
-            syntaxDescription = LDAPSyntaxDescriptionSyntax.decodeLDAPSyntax(
-                    v.getValue(),schema,false);
-          }
-          catch (DirectoryException de)
-          {
-            if (debugEnabled())
-            {
-              TRACER.debugCaught(DebugLogLevel.ERROR, de);
-            }
-
-            Message message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_LDAP_SYNTAX.get(
-                    schemaFile,
-                    de.getMessageObject());
-
-            if (failOnError)
-            {
-              throw new ConfigException(message, de);
-            }
-            else
-            {
-              logError(message);
-              continue;
-            }
-          }
-          catch (Exception e)
-          {
-            if (debugEnabled())
-            {
-              TRACER.debugCaught(DebugLogLevel.ERROR, e);
-            }
-
-            Message message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_LDAP_SYNTAX.get(
-                    schemaFile,
-                    v.getValue().toString() + ":  " + getExceptionMessage(e));
-
-            if (failOnError)
-            {
-              throw new ConfigException(message, e);
-            }
-            else
-            {
-              logError(message);
-              continue;
-            }
-          }
-
-           // Register it with the schema.  We will allow duplicates, with the
-          // later definition overriding any earlier definition, but we want
-          // to trap them and log a warning.
-          try
-          {
-            schema.registerLdapSyntaxDescription(
-                                  syntaxDescription, failOnError);
-          }
-          catch (DirectoryException de)
-          {
-            if (debugEnabled())
-            {
-              TRACER.debugCaught(DebugLogLevel.ERROR, de);
-            }
-
-            Message message = WARN_CONFIG_SCHEMA_CONFLICTING_LDAP_SYNTAX.get(
-                schemaFile, de.getMessageObject());
-            logError(message);
-
-            try
-            {
-              schema.registerLdapSyntaxDescription(syntaxDescription, true);
-            }
-            catch (Exception e)
-            {
-              // This should never happen.
-              if (debugEnabled())
-              {
-                TRACER.debugCaught(DebugLogLevel.ERROR, e);
-              }
-            }
-          }
-
-        }
-      }
-    }
-
-
     return mods;
   }
 
@@ -1509,12 +1512,12 @@
         attributeOid.equals("1.3.6.1.4.1.1466.101.120.16") ||
         attributeOid.equals("attributetypes-oid")      ||
         attributeOid.equals("objectclasses-oid")       ||
-        attributeOid.equals("matchingRules-oid")       ||
-        attributeOid.equals("matchingRuleUse-oid")     ||
-        attributeOid.equals("NameFormDescription-oid") ||
-        attributeOid.equals("dITContentRules-oid")     ||
-        attributeOid.equals("dITStructureRules") ||
-        attributeOid.equals("ldapSyntaxes-oid")
+        attributeOid.equals("matchingrules-oid")       ||
+        attributeOid.equals("matchingruleuse-oid")     ||
+        attributeOid.equals("nameformdescription-oid") ||
+        attributeOid.equals("ditcontentrules-oid")     ||
+        attributeOid.equals("ditstructurerules-oid") ||
+        attributeOid.equals("ldapsyntaxes-oid")
 
         )
     {
diff --git a/opends/src/server/org/opends/server/schema/LDAPSyntaxDescriptionSyntax.java b/opends/src/server/org/opends/server/schema/LDAPSyntaxDescriptionSyntax.java
index e0a4f74..0a890fd 100644
--- a/opends/src/server/org/opends/server/schema/LDAPSyntaxDescriptionSyntax.java
+++ b/opends/src/server/org/opends/server/schema/LDAPSyntaxDescriptionSyntax.java
@@ -29,6 +29,8 @@
 
 
 
+import java.util.regex.Pattern;
+
 import org.opends.server.admin.std.server.AttributeSyntaxCfg;
 import org.opends.server.api.ApproximateMatchingRule;
 import org.opends.server.api.AttributeSyntax;
@@ -496,8 +498,7 @@
     String oid = oidBuffer.toString();
     String description = descriptionBuffer.toString();
     StringBuilder extBuffer = new StringBuilder();
-    //Attribute syntax which will sustitute the syntax with oid.
-    AttributeSyntax subSyntax = null;
+    LDAPSyntaxDescriptionSyntax syntax = null;
 
     pos = readTokenName(valueStr, extBuffer, pos);
     String lowerTokenName = toLowerCase(extBuffer.toString());
@@ -507,7 +508,7 @@
       StringBuilder woidBuffer = new StringBuilder();
       pos = readQuotedString(lowerStr, woidBuffer, pos);
       String syntaxOID = woidBuffer.toString();
-      subSyntax = schema.getSyntax(syntaxOID);
+      AttributeSyntax subSyntax = schema.getSyntax(syntaxOID);
       if(subSyntax == null)
       {
         Message message = WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_SYNTAX.get(
@@ -515,6 +516,33 @@
         throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
                                      message);
       }
+      syntax = new SubstitutionSyntax(subSyntax,description,oid);
+    }
+    else if(lowerTokenName.equals("x-pattern"))
+    {
+      StringBuilder regexBuffer = new StringBuilder();
+      pos = readQuotedString(valueStr, regexBuffer, pos);
+      String regex = regexBuffer.toString();
+      if(regex == null)
+      {
+        Message message = WARN_ATTR_SYNTAX_LDAPSYNTAX_REGEX_NO_PATTERN.get(
+               valueStr);
+        throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+                message);
+      }
+
+      try
+      {
+        Pattern pattern = Pattern.compile(regex);
+        syntax = new RegexSyntax(pattern,description,oid);
+      }
+      catch(Exception e)
+      {
+        Message message = WARN_ATTR_SYNTAX_LDAPSYNTAX_REGEX_INVALID_PATTERN.get
+                (valueStr,regex);
+        throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+                message);
+      }
     }
     else
     {
@@ -557,16 +585,8 @@
       }
     }
 
-    LDAPSyntaxDescription syntaxDesc = null;
     //Since we reached here it means everything is OK.
-    if(subSyntax !=null)
-    {
-      //A SubstitutionSyntax is requested.
-      syntaxDesc = new LDAPSyntaxDescription(valueStr,
-              new SubstitutionSyntax(subSyntax,description,oid),
-              description,null);
-    }
-    return syntaxDesc;
+    return new LDAPSyntaxDescription(valueStr,syntax,description,null);
   }
 
 
@@ -920,7 +940,7 @@
 
 
 
-     /**
+    /**
      * {@inheritDoc}
      */
      @Override
@@ -966,67 +986,250 @@
 
 
 
-      /**
-   * Retrieves the default equality matching rule that will be used for
-   * attributes with this syntax.
-   *
-   * @return  The default equality matching rule that will be used for
-   *          attributes with this syntax, or <CODE>null</CODE> if equality
-   *          matches will not be allowed for this type by default.
-   */
-  @Override
-  public EqualityMatchingRule getEqualityMatchingRule()
-  {
-    return subSyntax.getEqualityMatchingRule();
+    /**
+     * Retrieves the default equality matching rule that will be used for
+     * attributes with this syntax.
+     *
+     * @return  The default equality matching rule that will be used for
+     *          attributes with this syntax, or <CODE>null</CODE> if equality
+     *          matches will not be allowed for this type by default.
+     */
+    @Override
+    public EqualityMatchingRule getEqualityMatchingRule()
+    {
+      return subSyntax.getEqualityMatchingRule();
+    }
+
+
+
+    /**
+     * Retrieves the default ordering matching rule that will be used for
+     * attributes with this syntax.
+     *
+     * @return  The default ordering matching rule that will be used for
+     *          attributes with this syntax, or <CODE>null</CODE> if ordering
+     *          matches will not be allowed for this type by default.
+     */
+    @Override
+    public OrderingMatchingRule getOrderingMatchingRule()
+    {
+      return subSyntax.getOrderingMatchingRule();
+    }
+
+
+
+    /**
+     * Retrieves the default substring matching rule that will be used for
+     * attributes with this syntax.
+     *
+     * @return  The default substring matching rule that will be used for
+     *          attributes with this syntax, or <CODE>null</CODE> if substring
+     *          matches will not be allowed for this type by default.
+     */
+    @Override
+    public SubstringMatchingRule getSubstringMatchingRule()
+    {
+      return subSyntax.getSubstringMatchingRule();
+    }
+
+
+
+    /**
+     * Retrieves the default approximate matching rule that will be used for
+     * attributes with this syntax.
+     *
+     * @return  The default approximate matching rule that will be used for
+     *          attributes with this syntax, or <CODE>null</CODE> if approximate
+     *          matches will not be allowed for this type by default.
+     */
+    @Override
+    public ApproximateMatchingRule getApproximateMatchingRule()
+    {
+      return subSyntax.getApproximateMatchingRule();
+    }
   }
 
 
 
   /**
-   * Retrieves the default ordering matching rule that will be used for
-   * attributes with this syntax.
-   *
-   * @return  The default ordering matching rule that will be used for
-   *          attributes with this syntax, or <CODE>null</CODE> if ordering
-   *          matches will not be allowed for this type by default.
+   * This class provides a regex mechanism where a new syntax and its
+   * corresponding matching rules can be created on-the-fly. A regex
+   * syntax is an LDAPSyntaxDescriptionSyntax with X-PATTERN extension.
    */
-  @Override
-  public OrderingMatchingRule getOrderingMatchingRule()
+  private static class RegexSyntax extends
+          LDAPSyntaxDescriptionSyntax
   {
-    return subSyntax.getOrderingMatchingRule();
-  }
+    // The Pattern associated with the regex.
+    private Pattern pattern;
+
+    // The description of this syntax.
+    private String description;
+
+    //The oid of this syntax.
+    private String oid;
+
+    //The equality matching rule.
+    private EqualityMatchingRule equalityMatchingRule;
+
+    //The substring matching rule.
+    private SubstringMatchingRule substringMatchingRule;
+
+    //The ordering matching rule.
+    private OrderingMatchingRule orderingMatchingRule;
+
+    //The approximate matching rule.
+    private ApproximateMatchingRule approximateMatchingRule;
+
+
+    //Creates a new instance of this syntax.
+    private RegexSyntax(Pattern pattern,
+            String description,
+            String oid)
+    {
+      super();
+      this.pattern = pattern;
+      this.description = description;
+      this.oid = oid;
+    }
 
 
 
-  /**
-   * Retrieves the default substring matching rule that will be used for
-   * attributes with this syntax.
-   *
-   * @return  The default substring matching rule that will be used for
-   *          attributes with this syntax, or <CODE>null</CODE> if substring
-   *          matches will not be allowed for this type by default.
-   */
-  @Override
-  public SubstringMatchingRule getSubstringMatchingRule()
-  {
-    return subSyntax.getSubstringMatchingRule();
-  }
+     /**
+     * {@inheritDoc}
+     */
+     @Override
+    public String getSyntaxName()
+    {
+      // There is no name for a regex syntax.
+      return null;
+    }
 
 
 
-  /**
-   * Retrieves the default approximate matching rule that will be used for
-   * attributes with this syntax.
-   *
-   * @return  The default approximate matching rule that will be used for
-   *          attributes with this syntax, or <CODE>null</CODE> if approximate
-   *          matches will not be allowed for this type by default.
-   */
-  @Override
-  public ApproximateMatchingRule getApproximateMatchingRule()
-  {
-    return subSyntax.getApproximateMatchingRule();
-  }
+    /**
+     * {@inheritDoc}
+     */
+     @Override
+    public String getOID()
+    {
+      return oid;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+     @Override
+    public String getDescription()
+    {
+      return description;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean valueIsAcceptable(ByteSequence value,
+                                     MessageBuilder invalidReason)
+    {
+      String strValue = value.toString();
+      boolean matches = pattern.matcher(strValue).matches();
+      if(!matches)
+      {
+        Message message = WARN_ATTR_SYNTAX_LDAPSYNTAX_REGEX_INVALID_VALUE.get(
+                strValue,pattern.pattern());
+        invalidReason.append(message);
+      }
+      return matches;
+    }
+
+
+
+    /**
+     * Retrieves the default equality matching rule that will be used for
+     * attributes with this syntax.
+     *
+     * @return  The default equality matching rule that will be used for
+     *          attributes with this syntax, or <CODE>null</CODE> if equality
+     *          matches will not be allowed for this type by default.
+     */
+    @Override
+    public EqualityMatchingRule getEqualityMatchingRule()
+    {
+      if(equalityMatchingRule == null)
+      {
+        //This has already been verified.
+        equalityMatchingRule =
+                DirectoryServer.getEqualityMatchingRule(EMR_CASE_IGNORE_OID);
+      }
+      return equalityMatchingRule;
+    }
+
+
+
+    /**
+     * Retrieves the default ordering matching rule that will be used for
+     * attributes with this syntax.
+     *
+     * @return  The default ordering matching rule that will be used for
+     *          attributes with this syntax, or <CODE>null</CODE> if ordering
+     *          matches will not be allowed for this type by default.
+     */
+    @Override
+    public OrderingMatchingRule getOrderingMatchingRule()
+    {
+      if(orderingMatchingRule == null)
+      {
+        orderingMatchingRule =
+                DirectoryServer.getOrderingMatchingRule(OMR_CASE_IGNORE_OID);
+      }
+      return orderingMatchingRule;
+    }
+
+
+
+    /**
+     * Retrieves the default substring matching rule that will be used for
+     * attributes with this syntax.
+     *
+     * @return  The default substring matching rule that will be used for
+     *          attributes with this syntax, or <CODE>null</CODE> if substring
+     *          matches will not be allowed for this type by default.
+     */
+    @Override
+    public SubstringMatchingRule getSubstringMatchingRule()
+    {
+      if(substringMatchingRule == null)
+      {
+        substringMatchingRule =
+                DirectoryServer.getSubstringMatchingRule(SMR_CASE_IGNORE_OID);
+      }
+      return substringMatchingRule;
+    }
+
+
+
+    /**
+     * Retrieves the default approximate matching rule that will be used for
+     * attributes with this syntax.
+     *
+     * @return  The default approximate matching rule that will be used for
+     *          attributes with this syntax, or <CODE>null</CODE> if approximate
+     *          matches will not be allowed for this type by default.
+     */
+    @Override
+    public ApproximateMatchingRule getApproximateMatchingRule()
+    {
+      if(approximateMatchingRule == null)
+      {
+        approximateMatchingRule =
+                DirectoryServer.getApproximateMatchingRule(
+                                    AMR_DOUBLE_METAPHONE_OID);
+      }
+      return approximateMatchingRule;
+    }
   }
 }
-
diff --git a/opends/src/server/org/opends/server/types/Schema.java b/opends/src/server/org/opends/server/types/Schema.java
index 011280f..e5a3f22 100644
--- a/opends/src/server/org/opends/server/types/Schema.java
+++ b/opends/src/server/org/opends/server/types/Schema.java
@@ -3386,9 +3386,11 @@
            new LinkedHashSet<String>();
       LinkedHashSet<String> matchingRuleUses =
            new LinkedHashSet<String>();
+      LinkedHashSet<String> ldapSyntaxes =
+           new LinkedHashSet<String>();
       genConcatenatedSchema(attributeTypes, objectClasses, nameForms,
                             ditContentRules, ditStructureRules,
-                            matchingRuleUses);
+                            matchingRuleUses,ldapSyntaxes);
 
 
       File configFile = new File(DirectoryServer.getConfigFile());
@@ -3459,6 +3461,15 @@
         writer.newLine();
       }
 
+
+      for (String line : ldapSyntaxes)
+      {
+        writer.write(ATTR_LDAP_SYNTAXES);
+        writer.write(": ");
+        writer.write(line);
+        writer.newLine();
+      }
+
       writer.close();
 
       if (concatFile.exists())
@@ -3506,6 +3517,9 @@
    * @param  matchingRuleUses   The set into which to place the
    *                            matching rule uses read from the
    *                            schema files.
+   * @param ldapSyntaxes The set into which to place the
+   *                            ldap syntaxes read from the
+   *                            schema files.
    *
    * @throws  IOException  If a problem occurs while reading the
    *                       schema file elements.
@@ -3516,7 +3530,8 @@
                           LinkedHashSet<String> nameForms,
                           LinkedHashSet<String> ditContentRules,
                           LinkedHashSet<String> ditStructureRules,
-                          LinkedHashSet<String> matchingRuleUses)
+                          LinkedHashSet<String> matchingRuleUses,
+                          LinkedHashSet<String> ldapSyntaxes)
           throws IOException
   {
     // Get a sorted list of the files in the schema directory.
@@ -3641,6 +3656,12 @@
                      ATTR_MATCHING_RULE_USE.length()+1).trim();
           matchingRuleUses.add(value);
         }
+        else if(lowerLine.startsWith(ATTR_LDAP_SYNTAXES_LC))
+        {
+          value = line.substring(
+                     ATTR_LDAP_SYNTAXES.length()+1).trim();
+          ldapSyntaxes.add(value);
+        }
       }
     }
   }
@@ -3671,6 +3692,9 @@
    * @param  matchingRuleUses   The set into which to place the
    *                            matching rule uses read from the
    *                            concatenated schema file.
+   * @param ldapSyntaxes The set into which to place the
+   *                            ldap syntaxes read from the
+   *                            concatenated schema file.
    *
    * @throws  IOException  If a problem occurs while reading the
    *                       schema file elements.
@@ -3681,7 +3705,8 @@
                           LinkedHashSet<String> nameForms,
                           LinkedHashSet<String> ditContentRules,
                           LinkedHashSet<String> ditStructureRules,
-                          LinkedHashSet<String> matchingRuleUses)
+                          LinkedHashSet<String> matchingRuleUses,
+                          LinkedHashSet<String> ldapSyntaxes)
           throws IOException
   {
     BufferedReader reader =
@@ -3730,6 +3755,12 @@
                      ATTR_MATCHING_RULE_USE.length()+1).trim();
         matchingRuleUses.add(value);
       }
+      else if (lowerLine.startsWith(ATTR_LDAP_SYNTAXES_LC))
+      {
+        value = line.substring(
+                  ATTR_LDAP_SYNTAXES.length()+1).trim();
+        ldapSyntaxes.add(value);
+      }
     }
 
     reader.close();
@@ -3957,6 +3988,12 @@
       extensibleMatchingRules = null;
     }
 
+    if(ldapSyntaxDescriptions != null)
+    {
+      ldapSyntaxDescriptions.clear();
+      ldapSyntaxDescriptions = null;
+    }
+
   }
 }
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/LDAPSyntaxTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/LDAPSyntaxTest.java
index ce6efae..e7fabb7 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/LDAPSyntaxTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/LDAPSyntaxTest.java
@@ -32,6 +32,7 @@
 import java.util.List;
 import org.opends.server.TestCaseUtils;
 import org.opends.server.api.AttributeSyntax;
+import org.opends.server.core.AddOperation;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.protocols.internal.InternalSearchOperation;
@@ -40,6 +41,7 @@
 import org.opends.server.types.AttributeValue;
 import org.opends.server.types.ByteString;
 import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.Entry;
 import org.opends.server.types.ResultCode;
 import org.opends.server.types.SearchResultEntry;
 import org.opends.server.types.SearchScope;
@@ -136,71 +138,70 @@
   /**
    * Tests whether an implemented syntax can't be substituted by another.
    */
-   @Test()
-   public void testSubstitutionSyntaxForInvalidSubstitution() throws Exception
-   {
+  @Test()
+  public void testSubstitutionSyntaxForInvalidSubstitution() throws Exception
+  {
+    try
+    {
+      //Test if we can substitute a directory string syntax by itself.
+      int resultCode = TestCaseUtils.applyModifications(true,
+        "dn: cn=schema",
+        "changetype: modify",
+        "add: ldapsyntaxes",
+        "ldapsyntaxes: ( 1.3.6.1.4.1.1466.115.121.1.15 " +
+        "DESC 'Replacing DirectorySyntax'   " +
+        " X-SUBST '1.3.6.1.4.1.1466.115.121.1.15' )");
 
-     try
-     {
-       //Test if we can substitute a directory string syntax by itself.
-        int resultCode = TestCaseUtils.applyModifications(true,
-          "dn: cn=schema",
-          "changetype: modify",
-          "add: ldapsyntaxes",
-          "ldapsyntaxes: ( 1.3.6.1.4.1.1466.115.121.1.15 " +
-          "DESC 'Replacing DirectorySyntax'   " +
-          " X-SUBST '1.3.6.1.4.1.1466.115.121.1.15' )");
+      //This is not expected to happen
+      assertFalse(resultCode==0);
 
-        //This is not expected to happen
-        assertFalse(resultCode==0);
+      //Test if we can substitute a directory string syntax by an undefined.
+      resultCode = TestCaseUtils.applyModifications(true,
+        "dn: cn=schema",
+        "changetype: modify",
+        "add: ldapsyntaxes",
+        "ldapsyntaxes: ( 1.3.6.1.4.1.1466.115.121.1.15 " +
+        "DESC 'Replacing DirectorySyntax'   " +
+        " X-SUBST '1.1.1' )");
 
-        //Test if we can substitute a directory string syntax by an undefined.
-        resultCode = TestCaseUtils.applyModifications(true,
-          "dn: cn=schema",
-          "changetype: modify",
-          "add: ldapsyntaxes",
-          "ldapsyntaxes: ( 1.3.6.1.4.1.1466.115.121.1.15 " +
-          "DESC 'Replacing DirectorySyntax'   " +
-          " X-SUBST '1.1.1' )");
-
-        //This is not expected to happen
-        assertFalse(resultCode==0);
+      //This is not expected to happen
+      assertFalse(resultCode==0);
 
 
-        //Test if we can substitute a core syntax with a user-defined
-        //syntax
-        addSubtitutionSyntax();
-        //Replace the IA5Stringsyntax with the custom syntax we just created.
-         resultCode = TestCaseUtils.applyModifications(true,
-          "dn: cn=schema",
-          "changetype: modify",
-          "add: ldapsyntaxes",
-          "ldapsyntaxes: ( 1.3.6.1.4.1.1466.115.121.1.26 " +
-          "DESC 'Replacing DirectorySyntax'   " +
-          " X-SUBST '9.9.9' )");
+      //Test if we can substitute a core syntax with a user-defined
+      //syntax
+      addSubtitutionSyntax();
+      //Replace the IA5Stringsyntax with the custom syntax we just created.
+      resultCode = TestCaseUtils.applyModifications(true,
+        "dn: cn=schema",
+        "changetype: modify",
+        "add: ldapsyntaxes",
+        "ldapsyntaxes: ( 1.3.6.1.4.1.1466.115.121.1.26 " +
+        "DESC 'Replacing DirectorySyntax'   " +
+        " X-SUBST '9.9.9' )");
 
-        //This is not expected to happen
-        assertFalse(resultCode==0);
-     }
-     finally
-     {
+      //This is not expected to happen
+      assertFalse(resultCode==0);
+    }
+    finally
+    {
       deleteSubstitutionSyntax();
-     }
-   }
+    }
+  }
 
 
 
-   /**
+  /**
     * Tests whether both the virtual and the newly added real substitution
     * sytanx are available when a search is made for ldapsyntaxes attribute.
     *
     * @throws java.lang.Exception
     */
-   @Test()
-   public void testSubstitutionSyntaxSearch() throws Exception
-   {
-     try
-     {
+  @Test()
+  public void testSubstitutionSyntaxSearch() throws Exception
+  {
+    try
+    {
       addSubtitutionSyntax();
       InternalClientConnection conn =
       InternalClientConnection.getRootConnection();
@@ -230,9 +231,9 @@
       assertNotNull(e);
       Attribute attr = e.getAttribute("ldapsyntaxes").get(0);
       Iterator<AttributeValue> iter = attr.iterator();
-      
+
       //There are other ways of doing it but we will extract the OID
-      //from the attribute values and then check to see if our 
+      //from the attribute values and then check to see if our
       //OID is found in the result set or not.
       List<String> syntaxList = new ArrayList<String>();
       while(iter.hasNext())
@@ -241,21 +242,21 @@
         //parse the OIDs.
         syntaxList.add(getOIDFromLdapSyntax(val.toString()));
       }
- 
+
       assertTrue(syntaxList.size() ==
               DirectoryServer.getAttributeSyntaxSet().size() ) ;
-      //Check if we find our OID.    
+      //Check if we find our OID.
       assertTrue(syntaxList.contains("9.9.9"));
       //DirectoryString.
       assertTrue(syntaxList.contains("1.3.6.1.4.1.1466.115.121.1.15"));
       //IA5String.
       assertTrue(syntaxList.contains("1.3.6.1.4.1.1466.115.121.1.26"));
-     }
-     finally
-     {
-       deleteSubstitutionSyntax();
-     }
-   }
+    }
+    finally
+    {
+      deleteSubstitutionSyntax();
+    }
+  }
 
 
 
@@ -265,6 +266,7 @@
     *
     * @throws java.lang.Exception
     */
+   @Test()
    public void testSubsitutionSyntaxAddValues() throws Exception
    {
      try
@@ -298,6 +300,109 @@
    }
 
 
+
+  /**
+    * Tests whether it is possible to add values after a regex syntax
+    * has been added.
+    *
+    * @throws java.lang.Exception
+    */
+  @Test()
+  public void testRegexSyntaxAddValues() throws Exception
+  {
+    try
+    {
+      addRegexSyntax();
+      TestCaseUtils.initializeTestBackend(true);
+
+      //This addition should fail because it doesn't match the pattern.
+      Entry entry = TestCaseUtils.makeEntry(
+      "dn: cn=syntax-test,o=test",
+      "objectclass: person",
+      "objectclass: testOC",
+      "cn: syntax-test",
+      "sn: xyz",
+      "test-attr-regex: invalid regex");
+      InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    AddOperation addOperation = conn.processAdd(entry.getDN(),
+                                     entry.getObjectClasses(),
+                                     entry.getUserAttributes(),
+                                     entry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(),
+            ResultCode.INVALID_ATTRIBUTE_SYNTAX);
+
+      //This addition should go through.
+      TestCaseUtils.addEntry(
+        "dn: cn=syntax-test,o=test",
+        "objectclass: person",
+        "objectclass: testOC",
+        "cn: syntax-test",
+        "sn: xyz",
+        "test-attr-regex: host:0.0.0");
+    }
+    finally
+    {
+     deleteRegexSyntax();
+    }
+  }
+
+
+
+  /**
+   * Tests the search using regex syntax.
+   *
+   * @throws java.lang.Exception
+   */
+  @Test()
+  public void testRegexSyntaxSearch() throws Exception
+  {
+    try
+    {
+      addRegexSyntax();
+      //This addition should go through.
+      TestCaseUtils.addEntry(
+        "dn: cn=test,o=test",
+        "objectclass: person",
+        "objectclass: testOC",
+        "cn: test",
+        "sn: xyz",
+        "test-attr-regex: host:0.0.0");
+
+      InternalClientConnection conn =
+      InternalClientConnection.getRootConnection();
+
+      InternalSearchOperation searchOperation =
+           new InternalSearchOperation(
+                conn,
+                InternalClientConnection.nextOperationID(),
+                InternalClientConnection.nextMessageID(),
+                null,
+                ByteString.valueOf("cn=test,o=test"),
+                SearchScope.WHOLE_SUBTREE,
+                DereferencePolicy.NEVER_DEREF_ALIASES,
+                Integer.MAX_VALUE,
+                Integer.MAX_VALUE,
+                false,
+                LDAPFilter.decode("test-attr-regex=host:0.0.0"),
+                null, null);
+
+      searchOperation.run();
+      assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
+      List<SearchResultEntry> entries = searchOperation.getSearchEntries();
+      SearchResultEntry e = entries.get(0);
+      //An entry must be returned.
+      assertNotNull(e);
+    }
+    finally
+    {
+      deleteRegexSyntax();
+    }
+  }
+
+
+
   //Parses the OID from the syntax defitions.
   private String getOIDFromLdapSyntax(String valueStr)
   {
@@ -319,18 +424,9 @@
     }
     int oidStartPos = pos;
 
-    boolean lastWasPeriod = false;
     while ((pos < length) && ((c = valueStr.charAt(pos)) != ' ')
           && (c = valueStr.charAt(pos)) != ')')
     {
-      if (c == '.')
-      {
-        lastWasPeriod = true;
-      }
-      else
-      {
-        lastWasPeriod = false;
-      }
       pos++;
     }
     return valueStr.substring(oidStartPos, pos);
@@ -366,4 +462,57 @@
 
     assertTrue(resultCode==0);
   }
+
+
+   //Adds a regex syntax to the schema.
+  private void addRegexSyntax() throws Exception
+  {
+    //Add the substitution syntax for an unimplemented syntax.
+    int resultCode = TestCaseUtils.applyModifications(true,
+    "dn: cn=schema",
+    "changetype: modify",
+    "add: ldapsyntaxes",
+    "ldapSyntaxes: ( 1.1.1 DESC 'Host and Port in the format of HOST:PORT'  " +
+            "X-PATTERN '^[a-z-A-Z]+:[0-9.]+\\d$' )");
+    assertTrue(resultCode==0);
+
+    resultCode = TestCaseUtils.applyModifications(true,
+          "dn: cn=schema",
+          "changetype: modify",
+          "add: attributetypes",
+          "attributetypes: ( test-attr-oid NAME 'test-attr-regex' SYNTAX 1.1.1 )",
+          "-",
+          "add: objectclasses",
+          "objectclasses: ( oc-oid NAME 'testOC' SUP top AUXILIARY MUST test-attr-regex)"
+        );
+    assertTrue(resultCode == 0);
+  }
+
+
+
+  //Deletes the regex syntax from the schema.
+  private void deleteRegexSyntax() throws Exception
+  {
+    //delete the substitution syntax.
+    int resultCode = TestCaseUtils.applyModifications(true,
+      "dn: cn=schema",
+      "changetype: modify",
+      "delete: objectclasses",
+      "objectclasses: ( oc-oid NAME 'testOC' SUP top AUXILIARY MUST test-attr-regex)",
+      "-",
+      "delete: attributetypes",
+      "attributetypes: ( test-attr-oid NAME 'test-attr-regex' SYNTAX 1.1.1 )"
+    );
+
+    assertTrue(resultCode==0);
+
+    resultCode = TestCaseUtils.applyModifications(true,
+    "dn: cn=schema",
+    "changetype: modify",
+    "delete: ldapsyntaxes",
+    "ldapSyntaxes: ( 1.1.1 DESC 'Host and Port in the format of HOST:PORT'  " +
+            "X-PATTERN '^[a-z-A-Z]+:[0-9.]+\\d$' )");
+
+    assertTrue(resultCode==0);
+  }
 }

--
Gitblit v1.10.0