From 87baa7b76fab7dadc76a6d7e84f9ab8a8b0f319d Mon Sep 17 00:00:00 2001
From: sin <sin@localhost>
Date: Fri, 10 Jul 2009 18:58:37 +0000
Subject: [PATCH] issue 4102: Provide implementation for substitution syntax
---
opendj-sdk/opends/src/server/org/opends/server/core/SchemaConfigManager.java | 144 ++++
opendj-sdk/opends/src/server/org/opends/server/types/LDAPSyntaxDescription.java | 467 ++++++++++++++++
opendj-sdk/opends/src/messages/messages/backend.properties | 10
opendj-sdk/opends/src/server/org/opends/server/types/Schema.java | 147 +++++
opendj-sdk/opends/src/server/org/opends/server/schema/LDAPSyntaxDescriptionSyntax.java | 440 ++++++++++++--
opendj-sdk/opends/src/messages/messages/schema.properties | 6
opendj-sdk/opends/src/server/org/opends/server/types/Entry.java | 11
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/LDAPSyntaxTest.java | 255 ++++++++
opendj-sdk/opends/src/messages/messages/config.properties | 5
opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java | 203 +++++++
10 files changed, 1,617 insertions(+), 71 deletions(-)
diff --git a/opendj-sdk/opends/src/messages/messages/backend.properties b/opendj-sdk/opends/src/messages/messages/backend.properties
index fdc0654..312c05f 100644
--- a/opendj-sdk/opends/src/messages/messages/backend.properties
+++ b/opendj-sdk/opends/src/messages/messages/backend.properties
@@ -1140,3 +1140,13 @@
because its dependency task %s is missing
NOTICE_TASK_STARTED_413=%s task %s started execution
NOTICE_TASK_FINISHED_414=%s task %s finished execution
+MILD_ERR_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_LDAP_SYNTAX_415=Unable to \
+ add ldap syntax description with OID %s because it conflicts with an existing ldap syntax description
+MILD_ERR_SCHEMA_MODIFY_REMOVE_NO_SUCH_LSD_416=Unable to remove ldap syntax \
+ description %s from the server schema because no such ldap syntax \
+ description is defined
+MILD_ERR_ATTR_SYNTAX_INVALID_SUBSTITUTION_SYNTAX_417=The provided value "%s" \
+ could not be parsed as a substitution syntax because its OID %s corresponds \
+ to an attribute syntax that is already implemented
+MILD_ERR_SCHEMA_MODIFY_CANNOT_DECODE_LDAP_SYNTAX_418=An error occurred while \
+ attempting to decode the ldapsyntax description "%s": %s
diff --git a/opendj-sdk/opends/src/messages/messages/config.properties b/opendj-sdk/opends/src/messages/messages/config.properties
index 458ada6..35fc146 100644
--- a/opendj-sdk/opends/src/messages/messages/config.properties
+++ b/opendj-sdk/opends/src/messages/messages/config.properties
@@ -2155,3 +2155,8 @@
SEVERE_ERR_CONFIG_NETWORK_GROUP_POLICY_CANNOT_INITIALIZE_722=An error occurred \
while trying to initialize a network group policy loaded from class %s with \
the information in configuration entry %s: %s
+SEVERE_WARN_CONFIG_SCHEMA_CANNOT_PARSE_LDAP_SYNTAX_723=An ldapSyntaxes \
+ attribute read from schema configuration file %s could not be parsed: %s
+SEVERE_WARN_CONFIG_SCHEMA_CONFLICTING_LDAP_SYNTAX_724=An ldap syntax read \
+ from schema configuration file %s conflicts with another ldap syntax already \
+ read into the schema: %s. The later ldap syntax description will be used
diff --git a/opendj-sdk/opends/src/messages/messages/schema.properties b/opendj-sdk/opends/src/messages/messages/schema.properties
index bf4c5a7..35e5641 100644
--- a/opendj-sdk/opends/src/messages/messages/schema.properties
+++ b/opendj-sdk/opends/src/messages/messages/schema.properties
@@ -995,4 +995,10 @@
MILD_WARN_ATTR_CONFLICTING_ASSERTION_FORMAT_304=The provided \
value "%s" could not be parsed as a valid assertion value because more than \
one time units are not allowed
+MILD_WARN_ATTR_LDAP_SYNTAX_ILLEGAL_CHAR_IN_OID_305=The provided value "%s" \
+ could not be parsed as an ldap syntax because the OID contained an illegal \
+ character %s at position %d
+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
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java b/opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java
index 056da63..a69218f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -78,6 +78,7 @@
import org.opends.server.schema.DITContentRuleSyntax;
import org.opends.server.schema.DITStructureRuleSyntax;
import org.opends.server.schema.GeneralizedTimeSyntax;
+import org.opends.server.schema.LDAPSyntaxDescriptionSyntax;
import org.opends.server.schema.MatchingRuleUseSyntax;
import org.opends.server.schema.NameFormSyntax;
import org.opends.server.schema.ObjectClassSyntax;
@@ -1270,6 +1271,34 @@
addMatchingRuleUse(mru, newSchema, modifiedSchemaFiles);
}
}
+ else if(at.equals(ldapSyntaxesType))
+ {
+ for(AttributeValue v : a)
+ {
+ LDAPSyntaxDescription lsd = null;
+ try
+ {
+ lsd = LDAPSyntaxDescriptionSyntax.decodeLDAPSyntax(
+ v.getValue(),
+ newSchema, false);
+ }
+ catch(DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ Message message =
+ ERR_SCHEMA_MODIFY_CANNOT_DECODE_LDAP_SYNTAX.get(
+ v.getValue().toString(), de.getMessageObject());
+ throw new DirectoryException(
+ ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
+ de);
+ }
+ addLdapSyntaxDescription(lsd,newSchema,modifiedSchemaFiles);
+ }
+ }
else
{
Message message =
@@ -1456,6 +1485,34 @@
modifiedSchemaFiles);
}
}
+ else if (at.equals(ldapSyntaxesType))
+ {
+ for(AttributeValue v : a)
+ {
+ LDAPSyntaxDescription lsd = null;
+ try
+ {
+ lsd = LDAPSyntaxDescriptionSyntax.decodeLDAPSyntax(
+ v.getValue(),
+ newSchema, false);
+ }
+ catch(DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ Message message =
+ ERR_SCHEMA_MODIFY_CANNOT_DECODE_LDAP_SYNTAX.get(
+ v.getValue().toString(), de.getMessageObject());
+ throw new DirectoryException(
+ ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
+ de);
+ }
+ removeLdapSyntaxDescription(lsd,newSchema,modifiedSchemaFiles);
+ }
+ }
else
{
Message message =
@@ -3229,6 +3286,125 @@
/**
+ * Handles all processing required for adding the provided ldap syntax
+ * description to the given schema, replacing an existing ldap syntax
+ * description if necessary, and ensuring all other metadata is properly
+ * updated.
+ *
+ * @param ldapSyntaxDesc The ldap syntax description to add or replace in
+ * the server schema.
+ * @param schema The schema to which the name form should be
+ * added.
+ * @param modifiedSchemaFiles The names of the schema files containing
+ * schema elements that have been updated as part
+ * of the schema modification.
+ *
+ * @throws DirectoryException If a problem occurs while attempting to add
+ * the provided ldap syntax description to the
+ * server schema.
+ */
+ private void addLdapSyntaxDescription(LDAPSyntaxDescription ldapSyntaxDesc,
+ Schema schema,
+ Set<String> modifiedSchemaFiles)
+ throws DirectoryException
+ {
+ //Check if there is an existing syntax with this oid.
+ String oid = ldapSyntaxDesc.getLdapSyntaxDescriptionSyntax().getOID();
+
+ // We allow only unimplemented syntaxes to be substituted.
+ if(schema.getSyntax(oid) !=null)
+ {
+ Message message = ERR_ATTR_SYNTAX_INVALID_SUBSTITUTION_SYNTAX.get(
+ ldapSyntaxDesc.getDefinition(),oid);
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ message);
+ }
+
+ LDAPSyntaxDescription existingLSD =
+ schema.getLdapSyntaxDescription(oid);
+
+ // If there is no existing lsd, then we're adding a new ldapsyntax.
+ // Otherwise, we're replacing an existing one.
+ if (existingLSD == null)
+ {
+ schema.registerLdapSyntaxDescription(ldapSyntaxDesc, false);
+ String schemaFile = ldapSyntaxDesc.getSchemaFile();
+ if ((schemaFile == null) || (schemaFile.length() == 0))
+ {
+ schemaFile = FILE_USER_SCHEMA_ELEMENTS;
+ ldapSyntaxDesc.setSchemaFile(schemaFile);
+ }
+
+ modifiedSchemaFiles.add(schemaFile);
+ }
+ else
+ {
+ schema.deregisterLdapSyntaxDescription(existingLSD);
+ schema.registerLdapSyntaxDescription(ldapSyntaxDesc, false);
+ schema.rebuildDependentElements(existingLSD);
+
+ if ((ldapSyntaxDesc.getSchemaFile() == null) ||
+ (ldapSyntaxDesc.getSchemaFile().length() == 0))
+ {
+ String schemaFile = ldapSyntaxDesc.getSchemaFile();
+ if ((schemaFile == null) || (schemaFile.length() == 0))
+ {
+ schemaFile = FILE_USER_SCHEMA_ELEMENTS;
+ }
+
+ ldapSyntaxDesc.setSchemaFile(schemaFile);
+ modifiedSchemaFiles.add(schemaFile);
+ }
+ else
+ {
+ String newSchemaFile = ldapSyntaxDesc.getSchemaFile();
+ String oldSchemaFile = existingLSD.getSchemaFile();
+ if ((oldSchemaFile == null) || oldSchemaFile.equals(newSchemaFile))
+ {
+ modifiedSchemaFiles.add(newSchemaFile);
+ }
+ else
+ {
+ modifiedSchemaFiles.add(newSchemaFile);
+ modifiedSchemaFiles.add(oldSchemaFile);
+ }
+ }
+ }
+ }
+
+
+
+ //Gets rid of the ldap syntax description.
+ private void removeLdapSyntaxDescription(LDAPSyntaxDescription ldapSyntaxDesc,
+ Schema schema,
+ Set<String> modifiedSchemaFiles)
+ throws DirectoryException
+ {
+ //See if the specified ldap syntax description is actually defined in the
+ //server schema. If not, then fail. Note that we are checking only the
+ //real part of the ldapsyntaxes attribute. A virtual value is not searched
+ // and hence never deleted.
+ String oid = ldapSyntaxDesc.getLdapSyntaxDescriptionSyntax().getOID();
+ LDAPSyntaxDescription removeLSD = schema.getLdapSyntaxDescription(oid);
+
+ if ((removeLSD == null) || (! removeLSD.equals(ldapSyntaxDesc)))
+ {
+ Message message =
+ ERR_SCHEMA_MODIFY_REMOVE_NO_SUCH_LSD.get(oid);
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+ }
+
+ schema.deregisterLdapSyntaxDescription(removeLSD);
+ String schemaFile = removeLSD.getSchemaFile();
+ if (schemaFile != null)
+ {
+ modifiedSchemaFiles.add(schemaFile);
+ }
+ }
+
+
+
+ /**
* Creates an empty entry that may be used as the basis for a new schema file.
*
* @return An empty entry that may be used as the basis for a new schema
@@ -3439,6 +3615,33 @@
schemaEntry.putAttribute(matchingRuleUsesType, attrList);
}
+
+ /**
+ * 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/opendj-sdk/opends/src/server/org/opends/server/core/SchemaConfigManager.java b/opendj-sdk/opends/src/server/org/opends/server/core/SchemaConfigManager.java
index dfdd846..7aec8fb 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/SchemaConfigManager.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/SchemaConfigManager.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2008 Sun Microsystems, Inc.
+ * Copyright 2006-2009 Sun Microsystems, Inc.
*/
package org.opends.server.core;
import org.opends.messages.Message;
@@ -64,6 +64,8 @@
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.loggers.ErrorLogger.*;
import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.schema.LDAPSyntaxDescriptionSyntax;
+import org.opends.server.types.LDAPSyntaxDescription;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.schema.SchemaConstants.*;
import static org.opends.server.util.ServerConstants.*;
@@ -771,6 +773,47 @@
}
}
+ // 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())
@@ -1344,6 +1387,100 @@
}
+ // 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;
}
@@ -1369,13 +1506,16 @@
attributeOid.equals("2.5.21.7") ||
attributeOid.equals("2.5.21.8") ||
attributeOid.equals("2.5.4.3") ||
+ 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("dITStructureRules") ||
+ attributeOid.equals("ldapSyntaxes-oid")
+
)
{
return true;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/schema/LDAPSyntaxDescriptionSyntax.java b/opendj-sdk/opends/src/server/org/opends/server/schema/LDAPSyntaxDescriptionSyntax.java
index d36b900..e0a4f74 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/schema/LDAPSyntaxDescriptionSyntax.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/schema/LDAPSyntaxDescriptionSyntax.java
@@ -22,10 +22,10 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2008 Sun Microsystems, Inc.
+ * Copyright 2006-2009 Sun Microsystems, Inc.
*/
package org.opends.server.schema;
-import org.opends.messages.Message;
+
@@ -37,7 +37,7 @@
import org.opends.server.api.SubstringMatchingRule;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
-
+import org.opends.messages.Message;
import static org.opends.server.loggers.debug.DebugLogger.*;
import org.opends.server.loggers.debug.DebugTracer;
@@ -228,31 +228,18 @@
/**
- * Indicates whether the provided value is acceptable for use in an attribute
- * with this syntax. If it is not, then the reason may be appended to the
- * provided buffer.
- *
- * @param value The value for which to make the determination.
- * @param invalidReason The buffer to which the invalid reason should be
- * appended.
- *
- * @return <CODE>true</CODE> if the provided value is acceptable for use with
- * this syntax, or <CODE>false</CODE> if not.
+ * Parse the OID and Description fields from the ldap syntaxes.
*/
- @Override
- public boolean valueIsAcceptable(ByteSequence value,
- MessageBuilder invalidReason)
+ private static int parseOIDAndDescription(String valueStr,
+ StringBuilder descriptionBuffer, StringBuilder oidBuffer)
+ throws DirectoryException
{
- // Get string representations of the provided value using the provided form
- // and with all lowercase characters.
- String valueStr = value.toString();
- String lowerStr = toLowerCase(valueStr);
-
-
// We'll do this a character at a time. First, skip over any leading
// whitespace.
int pos = 0;
int length = valueStr.length();
+ String lowerStr = toLowerCase(valueStr);
+
while ((pos < length) && (valueStr.charAt(pos) == ' '))
{
pos++;
@@ -263,8 +250,9 @@
// This means that the value was empty or contained only whitespace. That
// is illegal.
- invalidReason.append(ERR_ATTR_SYNTAX_ATTRSYNTAX_EMPTY_VALUE.get());
- return false;
+ Message message = ERR_ATTR_SYNTAX_ATTRSYNTAX_EMPTY_VALUE.get();
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
}
@@ -274,10 +262,11 @@
if (c != '(')
{
- invalidReason.append(
+ Message message =
ERR_ATTR_SYNTAX_ATTRSYNTAX_EXPECTED_OPEN_PARENTHESIS.get(
- valueStr, (pos-1), String.valueOf(c)));
- return false;
+ valueStr, (pos-1), String.valueOf(c));
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
}
@@ -291,27 +280,30 @@
{
// This means that the end of the value was reached before we could find
// the OID. Ths is illegal.
- invalidReason.append(ERR_ATTR_SYNTAX_ATTRSYNTAX_TRUNCATED_VALUE.get(
- valueStr));
- return false;
+ Message message = ERR_ATTR_SYNTAX_ATTRSYNTAX_TRUNCATED_VALUE.get(
+ valueStr);
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
}
-
+ int oidStartPos = pos;
if (isDigit(c))
{
// This must be a numeric OID. In that case, we will accept only digits
// and periods, but not consecutive periods.
boolean lastWasPeriod = false;
- while ((pos < length) && ((c = valueStr.charAt(pos++)) != ' '))
+ while ((pos < length) && ((c = valueStr.charAt(pos)) != ' ')
+ && (c = valueStr.charAt(pos)) != ')')
{
if (c == '.')
{
if (lastWasPeriod)
{
- invalidReason.append(
- ERR_ATTR_SYNTAX_ATTRSYNTAX_DOUBLE_PERIOD_IN_NUMERIC_OID.get(
- valueStr, (pos-1)));
- return false;
+ Message message =
+ ERR_ATTR_SYNTAX_ATTRTYPE_DOUBLE_PERIOD_IN_NUMERIC_OID.
+ get(valueStr, (pos-1));
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
}
else
{
@@ -321,48 +313,56 @@
else if (! isDigit(c))
{
// This must have been an illegal character.
- invalidReason.append(
- ERR_ATTR_SYNTAX_ATTRSYNTAX_ILLEGAL_CHAR_IN_NUMERIC_OID.get(
- valueStr, String.valueOf(c), (pos-1)));
- return false;
+ Message message =
+ ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR_IN_NUMERIC_OID.
+ get(valueStr, String.valueOf(c), (pos-1));
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
}
else
{
lastWasPeriod = false;
}
+ pos++;
}
}
else
{
// This must be a "fake" OID. In this case, we will only accept
// alphabetic characters, numeric digits, and the hyphen.
- while ((pos < length) && ((c = valueStr.charAt(pos++)) != ' '))
+ while ((pos < length) && ((c = valueStr.charAt(pos)) != ' ')
+ && (c=valueStr.charAt(pos))!=')')
{
if (isAlpha(c) || isDigit(c) || (c == '-') ||
((c == '_') && DirectoryServer.allowAttributeNameExceptions()))
{
// This is fine. It is an acceptable character.
+ pos++;
}
else
{
// This must have been an illegal character.
-
- invalidReason.append(
- ERR_ATTR_SYNTAX_ATTRSYNTAX_ILLEGAL_CHAR_IN_STRING_OID.get(
- valueStr, String.valueOf(c), (pos-1)));
- return false;
+ Message message =
+ ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR_IN_STRING_OID.
+ get(valueStr, String.valueOf(c), (pos-1));
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
}
}
}
-
// If we're at the end of the value, then it isn't a valid attribute type
// description. Otherwise, parse out the OID.
if (pos >= length)
{
- invalidReason.append(ERR_ATTR_SYNTAX_ATTRSYNTAX_TRUNCATED_VALUE.get(
- valueStr));
- return false;
+ Message message = ERR_ATTR_SYNTAX_ATTRSYNTAX_TRUNCATED_VALUE.get(
+ valueStr);
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
+ }
+ else
+ {
+ oidBuffer.append(lowerStr.substring(oidStartPos, pos));
}
@@ -376,9 +376,10 @@
{
// This means that the end of the value was reached before we could find
// the OID. Ths is illegal.
- invalidReason.append(ERR_ATTR_SYNTAX_ATTRSYNTAX_TRUNCATED_VALUE.get(
- valueStr));
- return false;
+ Message message = ERR_ATTR_SYNTAX_ATTRSYNTAX_TRUNCATED_VALUE.get(
+ valueStr);
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
}
@@ -388,16 +389,15 @@
{
if (pos < length)
{
- invalidReason.append(
+ Message message =
ERR_ATTR_SYNTAX_ATTRSYNTAX_UNEXPECTED_CLOSE_PARENTHESIS.get(
- valueStr, (pos-1)));
- return false;
+ valueStr, (pos-1));
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
}
- return true;
}
-
// The next token must be "DESC" followed by a quoted string.
String tokenName;
try
@@ -413,24 +413,25 @@
TRACER.debugCaught(DebugLogLevel.ERROR, e);
}
- invalidReason.append(
+ Message message =
ERR_ATTR_SYNTAX_ATTRSYNTAX_CANNOT_READ_DESC_TOKEN.get(
- valueStr, pos, getExceptionMessage(e)));
- return false;
+ valueStr, pos, getExceptionMessage(e));
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
}
if (! tokenName.equals("desc"))
{
- invalidReason.append(ERR_ATTR_SYNTAX_ATTRSYNTAX_TOKEN_NOT_DESC.get(
- valueStr, tokenName));
- return false;
+ Message message = ERR_ATTR_SYNTAX_ATTRSYNTAX_TOKEN_NOT_DESC.get(
+ valueStr, tokenName);
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
}
// The next component must be the quoted description.
try
{
- StringBuilder descriptionBuffer = new StringBuilder();
pos = readQuotedString(valueStr, descriptionBuffer, pos);
}
catch (Exception e)
@@ -440,13 +441,173 @@
TRACER.debugCaught(DebugLogLevel.ERROR, e);
}
- invalidReason.append(
+ Message message =
ERR_ATTR_SYNTAX_ATTRSYNTAX_CANNOT_READ_DESC_VALUE.get(
- valueStr, pos, getExceptionMessage(e)));
+ valueStr, pos, getExceptionMessage(e));
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
+ }
+
+ return pos;
+
+ }
+
+
+
+ /**
+ * Decodes the contents of the provided byte sequence as an ldap syntax
+ * definition according to the rules of this syntax. Note that the provided
+ * byte sequence value does not need to be normalized (and in fact, it should
+ * not be in order to allow the desired capitalization to be preserved).
+ *
+ * @param value The byte sequence containing the value
+ * to decode (it does not need to be
+ * normalized).
+ * @param schema The schema to use to resolve references to
+ * other schema elements.
+ * @param allowUnknownElements Indicates whether to allow values that
+ * reference a superior class or required or
+ * optional attribute types which are not
+ * defined in the server schema. This should
+ * only be true when called by
+ * {@code valueIsAcceptable}.
+ *
+ * @return The decoded ldapsyntax definition.
+ *
+ * @throws DirectoryException If the provided value cannot be decoded as an
+ * ldapsyntax definition.
+ */
+ public static LDAPSyntaxDescription decodeLDAPSyntax(ByteSequence value,
+ Schema schema,
+ boolean allowUnknownElements) throws DirectoryException
+ {
+ // Get string representations of the provided value using the provided form
+ // and with all lowercase characters.
+ String valueStr = value.toString();
+ String lowerStr = toLowerCase(valueStr);
+ int length = valueStr.length();
+
+ StringBuilder descriptionBuffer = new StringBuilder();
+ StringBuilder oidBuffer = new StringBuilder();
+
+ //Retrieve the OID and Description part of the defition.
+ int pos = parseOIDAndDescription(valueStr, descriptionBuffer,oidBuffer);
+
+ String oid = oidBuffer.toString();
+ String description = descriptionBuffer.toString();
+ StringBuilder extBuffer = new StringBuilder();
+ //Attribute syntax which will sustitute the syntax with oid.
+ AttributeSyntax subSyntax = null;
+
+ pos = readTokenName(valueStr, extBuffer, pos);
+ String lowerTokenName = toLowerCase(extBuffer.toString());
+
+ if(lowerTokenName.equals("x-subst"))
+ {
+ StringBuilder woidBuffer = new StringBuilder();
+ pos = readQuotedString(lowerStr, woidBuffer, pos);
+ String syntaxOID = woidBuffer.toString();
+ subSyntax = schema.getSyntax(syntaxOID);
+ if(subSyntax == null)
+ {
+ Message message = WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_SYNTAX.get(
+ String.valueOf(oid), syntaxOID);
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ message);
+ }
+ }
+ else
+ {
+ Message message = WARN_ATTR_SYNTAX_LDAPSYNTAX_UNKNOWN_EXT.get(
+ valueStr,lowerTokenName,pos);
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
+ }
+
+ char c = valueStr.charAt(pos);
+
+ while ((pos < length) && (c == ' '))
+ {
+ pos++;
+ }
+
+ // The next character must be the closing parenthesis and there should not
+ // be anything after it (except maybe some spaces).
+ if ((c = valueStr.charAt(pos++)) != ')')
+ {
+
+ Message message =
+ ERR_ATTR_SYNTAX_ATTRSYNTAX_EXPECTED_CLOSE_PARENTHESIS.get(
+ valueStr, pos, String.valueOf(c));
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
+ }
+
+ while (pos < length)
+ {
+ c = valueStr.charAt(pos++);
+ if (c != ' ')
+ {
+
+ Message message =
+ ERR_ATTR_SYNTAX_ATTRSYNTAX_ILLEGAL_CHAR_AFTER_CLOSE.get(
+ valueStr, String.valueOf(c), pos);
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
+ }
+ }
+
+ 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;
+ }
+
+
+
+ /**
+ * Indicates whether the provided value is acceptable for use in an attribute
+ * with this syntax. If it is not, then the reason may be appended to the
+ * provided buffer.
+ *
+ * @param value The value for which to make the determination.
+ * @param invalidReason The buffer to which the invalid reason should be
+ * appended.
+ *
+ * @return <CODE>true</CODE> if the provided value is acceptable for use with
+ * this syntax, or <CODE>false</CODE> if not.
+ */
+ @Override
+ public boolean valueIsAcceptable(ByteSequence value,
+ MessageBuilder invalidReason)
+ {
+ // Get string representations of the provided value using the provided form
+ // and with all lowercase characters.
+ String valueStr = value.toString();
+ StringBuilder descriptionBuffer = new StringBuilder();
+ StringBuilder oidBuffer = new StringBuilder();
+
+ int length = valueStr.length();
+ int pos = 0;
+ try
+ {
+ pos = parseOIDAndDescription(valueStr, descriptionBuffer,oidBuffer);
+ }
+ catch(DirectoryException de)
+ {
+ invalidReason.append(de.getMessageObject());
return false;
}
+
+ char c = valueStr.charAt(pos);
//Check if we have a RFC 4512 style extension.
- if ((c = valueStr.charAt(pos)) != ')')
+ if (c != ')')
{
try {
pos=parseExtension(valueStr, pos);
@@ -724,5 +885,148 @@
{
return false;
}
+
+
+
+ /**
+ * This class provides a substitution mechanism where one unimplemented
+ * syntax can be substituted by another defined syntax. A substitution syntax
+ * is an LDAPSyntaxDescriptionSyntax with X-SUBST extension.
+ */
+ private static class SubstitutionSyntax extends
+ LDAPSyntaxDescriptionSyntax
+ {
+ // The syntax that will subsittute the unimplemented syntax.
+ private AttributeSyntax subSyntax;
+
+ // The description of this syntax.
+ private String description;
+
+ //The oid of this syntax.
+ private String oid;
+
+
+
+ //Creates a new instance of this syntax.
+ private SubstitutionSyntax(AttributeSyntax subSyntax,
+ String description,
+ String oid)
+ {
+ super();
+ this.subSyntax = subSyntax;
+ this.description = description;
+ this.oid = oid;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getSyntaxName()
+ {
+ // There is no name for a substitution syntax.
+ return null;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getOID()
+ {
+ return oid;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDescription()
+ {
+ return description;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean valueIsAcceptable(ByteSequence value,
+ MessageBuilder invalidReason)
+ {
+ return subSyntax.valueIsAcceptable(value, invalidReason);
+ }
+
+
+
+ /**
+ * 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();
+ }
+ }
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java b/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
index 3829f3a..dfb264e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
@@ -1659,6 +1659,17 @@
return allSuccessful;
}
+ AttributeType ldapSyntaxType =
+ DirectoryServer.getAttributeType(ATTR_LDAP_SYNTAXES_LC,
+ true);
+ if (attribute.getAttributeType().equals(ldapSyntaxType))
+ {
+ // This is tricky as ldapsyntaxes type is part real and part
+ // virtual. Don't do anything here and let the backend take
+ // care of it.
+ return true;
+ }
+
AttributeType attributeType = attribute.getAttributeType();
List<Attribute> attributes;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/LDAPSyntaxDescription.java b/opendj-sdk/opends/src/server/org/opends/server/types/LDAPSyntaxDescription.java
new file mode 100644
index 0000000..3e07676
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/LDAPSyntaxDescription.java
@@ -0,0 +1,467 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.
+ */
+
+
+package org.opends.server.types;
+
+
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+
+import org.opends.server.schema.LDAPSyntaxDescriptionSyntax;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.Validator.*;
+
+
+
+/**
+ * This class defines a data structure for storing and interacting
+ * with an ldap syntax, which defines the custom ldap syntaxes.
+ */
+@org.opends.server.types.PublicAPI(
+ stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
+ mayInstantiate=false,
+ mayExtend=false,
+ mayInvoke=true)
+
+public final class LDAPSyntaxDescription
+ implements SchemaFileElement
+{
+ // The set of additional name-value pairs associated with this ldap
+ // syntax definition.
+ private final Map<String,List<String>> extraProperties;
+
+ // The definition string used to create this ldap syntax
+ //description.
+ private final String definition;
+
+ // The description for this ldap syntax description.
+ private final String description;
+
+ // The OID of the enclosed ldap syntax description.
+ private final String oid;
+
+ //The LDAPSyntaxDescritpionSyntax associated with this ldap syntax.
+ private LDAPSyntaxDescriptionSyntax descriptionSyntax;
+
+
+
+ /**
+ * Creates a new ldap syntax definition with the provided
+ * information.
+ *
+ * @param definition The definition string used to create
+ * this ldap syntax. It must not be
+ * {@code null}.
+ * @param descriptionSyntax The ldap syntax description syntax
+ * associated with this ldap syntax.
+ * @param description The description for this ldap
+ * syntax.
+ * @param extraProperties A set of extra properties for this
+ * ldap syntax description.
+ */
+ public LDAPSyntaxDescription(String definition,
+ LDAPSyntaxDescriptionSyntax descriptionSyntax,
+ String description,
+ Map<String,List<String>> extraProperties)
+ {
+ ensureNotNull(definition,descriptionSyntax);
+
+ this.descriptionSyntax = descriptionSyntax;
+ this.oid = descriptionSyntax.getOID();
+ this.description = description;
+
+ int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME);
+ if (schemaFilePos > 0)
+ {
+ String defStr;
+ try
+ {
+ int firstQuotePos = definition.indexOf('\'', schemaFilePos);
+ int secondQuotePos = definition.indexOf('\'',
+ firstQuotePos+1);
+
+ defStr = definition.substring(0, schemaFilePos).trim() + " "
+ + definition.substring(secondQuotePos+1).trim();
+ }
+ catch (Exception e)
+ {
+ defStr = definition;
+ }
+
+ this.definition = defStr;
+ }
+ else
+ {
+ this.definition = definition;
+ }
+
+
+ if ((extraProperties == null) || extraProperties.isEmpty())
+ {
+ this.extraProperties =
+ new LinkedHashMap<String,List<String>>(0);
+ }
+ else
+ {
+ this.extraProperties =
+ new LinkedHashMap<String,List<String>>(extraProperties);
+ }
+ }
+
+
+
+ /**
+ * Retrieves the definition string used to create this ldap syntax
+ * description.
+ *
+ * @return The definition string used to create this ldap syntax
+ * description.
+ */
+ public String getDefinition()
+ {
+ return definition;
+ }
+
+
+
+ /**
+ * Retrieves the ldap syntax description syntax associated with
+ * this ldap syntax.
+ *
+ * @return The description syntax for this defition.
+ */
+ public LDAPSyntaxDescriptionSyntax getLdapSyntaxDescriptionSyntax()
+ {
+ return descriptionSyntax;
+ }
+
+
+
+ /**
+ * Creates a new instance of this ldap syntax based on the
+ * definition string. It will also preserve other state
+ * information associated with this ldap syntax that is not
+ * included in the definition string (e.g., the name of the schema
+ * file with which it is associated).
+ *
+ * @return The new instance of this ldap syntax based on the
+ * definition string.
+ *
+ * @throws DirectoryException If a problem occurs while
+ * attempting to create a new ldap
+ * syntax instance from the definition
+ * string.
+ */
+ public LDAPSyntaxDescription recreateFromDefinition()
+ throws DirectoryException
+ {
+ ByteString value = ByteString.valueOf(definition);
+ Schema schema = DirectoryConfig.getSchema();
+
+ LDAPSyntaxDescription ls =
+ LDAPSyntaxDescriptionSyntax.decodeLDAPSyntax(value,
+ schema, false);
+ ls.setSchemaFile(getSchemaFile());
+
+ return ls;
+ }
+
+
+
+ /**
+ * Retrieves the path to the schema file that contains the
+ * definition for this ldap syntax description.
+ *
+ * @return The path to the schema file that contains the
+ * definition for this ldap syntax description, or
+ * {@code null} if it is not known or if it is not stored
+ * in any schema file.
+ */
+ public String getSchemaFile()
+ {
+ List<String> values =
+ extraProperties.get(SCHEMA_PROPERTY_FILENAME);
+ if ((values == null) || values.isEmpty())
+ {
+ return null;
+ }
+
+ return values.get(0);
+ }
+
+
+
+ /**
+ * Specifies the path to the schema file that contains the
+ * definition for this ldap syntax description.
+ *
+ * @param schemaFile The path to the schema file that contains
+ * the definition for this ldap syntax description.
+ */
+ public void setSchemaFile(String schemaFile)
+ {
+ setExtraProperty(SCHEMA_PROPERTY_FILENAME, schemaFile);
+ }
+
+
+
+ /**
+ * Retrieves the description for this ldap syntax description.
+ *
+ * @return The description for this ldap syntax description, or
+ * {@code true} if there is none.
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+
+
+
+ /**
+ * Retrieves a mapping between the names of any extra non-standard
+ * properties that may be associated with this ldap syntax
+ * description and the value for that property.
+ *
+ * @return A mapping between the names of any extra non-standard
+ * properties that may be associated with this ldap syntax
+ * description and the value for that property.
+ */
+ public Map<String,List<String>> getExtraProperties()
+ {
+ return extraProperties;
+ }
+
+
+
+ /**
+ * Retrieves the value of the specified "extra" property for this
+ * ldap syntax description.
+ *
+ * @param propertyName The name of the "extra" property for which
+ * to retrieve the value.
+ *
+ * @return The value of the specified "extra" property for this
+ * ldap syntax description, or {@code null} if no such
+ * property is defined.
+ */
+ public List<String> getExtraProperty(String propertyName)
+ {
+ return extraProperties.get(propertyName);
+ }
+
+
+
+ /**
+ * Specifies the provided "extra" property for this ldap syntax
+ * description.
+ *
+ * @param name The name for the "extra" property. It must not
+ * be {@code null}.
+ * @param value The value for the "extra" property, or
+ * {@code null} if the property is to be removed.
+ */
+ public void setExtraProperty(String name, String value)
+ {
+ ensureNotNull(name);
+
+ if (value == null)
+ {
+ extraProperties.remove(name);
+ }
+ else
+ {
+ LinkedList<String> values = new LinkedList<String>();
+ values.add(value);
+
+ extraProperties.put(name, values);
+ }
+ }
+
+
+
+ /**
+ * Specifies the provided "extra" property for this ldap syntax
+ * description.
+ *
+ * @param name The name for the "extra" property. It must not
+ * be {@code null}.
+ * @param values The set of value for the "extra" property, or
+ * {@code null} if the property is to be removed.
+ */
+ public void setExtraProperty(String name, List<String> values)
+ {
+ ensureNotNull(name);
+
+ if ((values == null) || values.isEmpty())
+ {
+ extraProperties.remove(name);
+ }
+ else
+ {
+ LinkedList<String> valuesCopy = new LinkedList<String>(values);
+ extraProperties.put(name, valuesCopy);
+ }
+ }
+
+
+
+ /**
+ * Indicates whether the provided object is equal to this ldap
+ * syntax. The object will be considered equal if it is a ldap
+ * syntax with the same OID as the current ldap syntax description.
+ *
+ * @param o The object for which to make the determination.
+ *
+ * @return {@code true} if the provided object is equal to this
+ * ldap syntax description, or {@code true} if not.
+ */
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+
+ if ((o == null) || (! (o instanceof LDAPSyntaxDescription)))
+ {
+ return false;
+ }
+
+ return oid.equals(((LDAPSyntaxDescription) o).oid);
+ }
+
+
+
+ /**
+ * Retrieves the hash code for this ldap syntax description. It
+ * will be based on the sum of the bytes of the OID.
+ *
+ * @return The hash code for this ldap syntax description.
+ */
+ @Override
+ public int hashCode()
+ {
+ int oidLength = oid.length();
+ int hashCode = 0;
+ for (int i=0; i < oidLength; i++)
+ {
+ hashCode += oid.charAt(i);
+ }
+
+ return hashCode;
+ }
+
+
+
+ /**
+ * Retrieves the string representation of this ldap syntax
+ * description in the form specified in RFC 2252.
+ *
+ * @return The string representation of this ldap syntax in the
+ * form specified in RFC 2252.
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder buffer = new StringBuilder();
+ toString(buffer, true);
+ return buffer.toString();
+ }
+
+
+
+ /**
+ * Appends a string representation of this ldap syntax in the form
+ * specified in RFC 2252 to the provided buffer.
+ *
+ * @param buffer The buffer to which the information
+ * should be appended.
+ * @param includeFileElement Indicates whether to include an
+ * "extra" property that specifies the
+ * path to the schema file from which
+ * this ldap syntax was loaded.
+ */
+ public void toString(StringBuilder buffer,
+ boolean includeFileElement)
+ {
+ buffer.append("( ");
+ buffer.append(oid);
+
+ if ((description != null) && (description.length() > 0))
+ {
+ buffer.append(" DESC '");
+ buffer.append(description);
+ buffer.append("'");
+ }
+
+ if (! extraProperties.isEmpty())
+ {
+ for (String property : extraProperties.keySet())
+ {
+ if ((! includeFileElement) &&
+ property.equals(SCHEMA_PROPERTY_FILENAME))
+ {
+ continue;
+ }
+
+ List<String> valueList = extraProperties.get(property);
+
+ buffer.append(" ");
+ buffer.append(property);
+
+ if (valueList.size() == 1)
+ {
+ buffer.append(" '");
+ buffer.append(valueList.get(0));
+ buffer.append("'");
+ }
+ else
+ {
+ buffer.append(" ( ");
+
+ for (String value : valueList)
+ {
+ buffer.append("'");
+ buffer.append(value);
+ buffer.append("' ");
+ }
+
+ buffer.append(")");
+ }
+ }
+ }
+
+ buffer.append(" )");
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java b/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java
index fcca6a4..011280f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java
@@ -190,6 +190,11 @@
// names/OID and the name form itself.
private ConcurrentHashMap<String,NameForm> nameFormsByName;
+ // The set of ldap syntax descriptions for this schema, mapped
+ // the OID and the ldap syntax description itself.
+ private ConcurrentHashMap<String,LDAPSyntaxDescription>
+ ldapSyntaxDescriptions;
+
// The set of pre-encoded attribute syntax representations.
private LinkedHashSet<AttributeValue> syntaxSet;
@@ -263,6 +268,8 @@
nameFormsByOC =
new ConcurrentHashMap<ObjectClass,List<NameForm>>();
nameFormsByName = new ConcurrentHashMap<String,NameForm>();
+ ldapSyntaxDescriptions =
+ new ConcurrentHashMap<String,LDAPSyntaxDescription>();
subordinateTypes =
new ConcurrentHashMap<AttributeType,List<AttributeType>>();
@@ -906,6 +913,145 @@
/**
+ * Retrieves the ldap syntax definitions for this schema, as a
+ * mapping between the OID for the syntax and the ldap syntax
+ * defition itself. Each ldap syntax should only be present once,
+ * since its only key is its OID. The contents of the returned
+ * mapping must not be altered.
+ *
+ * @return The ldap syntax definitions for this schema.
+ */
+ public ConcurrentHashMap<String,LDAPSyntaxDescription>
+ getLdapSyntaxDescriptions()
+ {
+ return ldapSyntaxDescriptions;
+ }
+
+
+
+ /**
+ * Indicates whether this schema definition includes an ldap
+ * syntax description with the provided name or OID.
+ *
+ * @param lowerName The OID for which to make the
+ * determination, formatted in all lowercase
+ * characters.
+ *
+ * @return {@code true} if this schema contains an ldap syntax
+ * with the provided name or OID, or {@code false} if not.
+ */
+ public boolean hasLdapSyntaxDescription(String lowerName)
+ {
+ return ldapSyntaxDescriptions.containsKey(lowerName);
+ }
+
+
+
+ /**
+ * Retrieves the ldap syntax definition with the OID.
+ *
+ * @param lowerName The OID of the ldap syntax to retrieve,
+ * formatted in all lowercase characters.
+ *
+ * @return The requested ldap syntax, or <CODE>null</CODE> if
+ * no syntax is registered with the provided OID.
+ */
+ public LDAPSyntaxDescription getLdapSyntaxDescription(
+ String lowerName)
+ {
+ return ldapSyntaxDescriptions.get(lowerName);
+ }
+
+
+
+ /**
+ * Registers the provided ldap syntax description with this
+ * schema.
+ *
+ * @param syntax The ldap syntax description to register
+ * with this schema.
+ * @param overwriteExisting Indicates whether to overwrite an
+ * existing mapping if there are any
+ * conflicts (i.e., another ldap
+ * syntax with the same OID).
+ *
+ * @throws DirectoryException If a conflict is encountered and
+ * <CODE>overwriteExisting</CODE> flag
+ * is set to <CODE>false</CODE>
+ */
+ public void registerLdapSyntaxDescription(
+ LDAPSyntaxDescription syntax,
+ boolean overwriteExisting)
+ throws DirectoryException
+ {
+ /**
+ * ldapsyntaxes is part real and part virtual. For any
+ * ldapsyntaxes attribute this is real, an LDAPSyntaxDescription
+ * object is created and stored with the schema. Also, the
+ * associated LDAPSyntaxDescriptionSyntax is added into the
+ * virtual syntax set to make this available through virtual
+ * ldapsyntaxes attribute.
+ */
+ synchronized (ldapSyntaxDescriptions)
+ {
+ String oid = toLowerCase(
+ syntax.getLdapSyntaxDescriptionSyntax().getOID());
+ if (! overwriteExisting)
+ {
+ if (ldapSyntaxDescriptions.containsKey(oid))
+ {
+ Message message =
+ ERR_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_LDAP_SYNTAX.
+ get(oid);
+ throw new DirectoryException(
+ ResultCode.CONSTRAINT_VIOLATION, message);
+ }
+ }
+
+ ldapSyntaxDescriptions.put(oid, syntax);
+
+ //Register the attribute syntax with the schema. It will ensure
+ // syntax is available along with the other virtual values for
+ // ldapsyntaxes.
+ registerSyntax(syntax.getLdapSyntaxDescriptionSyntax(),
+ overwriteExisting);
+ }
+ }
+
+
+
+ /**
+ * Deregisters the provided ldap syntax description with this
+ * schema.
+ *
+ * @param syntax The ldap syntax to deregister with this
+ * schema.
+ */
+ public void deregisterLdapSyntaxDescription(
+ LDAPSyntaxDescription syntax)
+ {
+ synchronized (ldapSyntaxDescriptions)
+ {
+ //Remove the real value.
+ ldapSyntaxDescriptions.remove(
+ toLowerCase(syntax.getLdapSyntaxDescriptionSyntax().getOID()),
+ syntax);
+
+ try
+ {
+ //Get rid of this from the virtual ldapsyntaxes.
+ deregisterSyntax(syntax.getLdapSyntaxDescriptionSyntax());
+ }
+ catch (Exception e)
+ {
+ deregisterSyntax(syntax.getLdapSyntaxDescriptionSyntax());
+ }
+ }
+ }
+
+
+
+ /**
* Retrieves the entire set of matching rule definitions for this
* schema, as a mapping between the lowercase names and OIDs for the
* matching rule and the matching rule itself. Each matching rule
@@ -3173,6 +3319,7 @@
ditStructureRulesByNameForm);
dupSchema.nameFormsByOC.putAll(nameFormsByOC);
dupSchema.nameFormsByName.putAll(nameFormsByName);
+ dupSchema.ldapSyntaxDescriptions.putAll(ldapSyntaxDescriptions);
dupSchema.syntaxSet.addAll(syntaxSet);
dupSchema.attributeTypeSet.addAll(attributeTypeSet);
dupSchema.ditContentRuleSet.addAll(ditContentRuleSet);
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/LDAPSyntaxTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/LDAPSyntaxTest.java
index 92e6435..ce6efae 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/LDAPSyntaxTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/LDAPSyntaxTest.java
@@ -22,12 +22,30 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2008 Sun Microsystems, Inc.
+ * Copyright 2006-2009 Sun Microsystems, Inc.
*/
package org.opends.server.schema;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import org.opends.server.TestCaseUtils;
import org.opends.server.api.AttributeSyntax;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.protocols.ldap.LDAPFilter;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchScope;
import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
/**
* Test the LDAPSyntaxDescriptionSyntax.
@@ -113,4 +131,239 @@
};
}
+
+
+ /**
+ * Tests whether an implemented syntax can't be substituted by another.
+ */
+ @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' )");
+
+ //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' )");
+
+ //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' )");
+
+ //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
+ {
+ addSubtitutionSyntax();
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>();
+ attrList.add("ldapsyntaxes");
+
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(
+ conn,
+ InternalClientConnection.nextOperationID(),
+ InternalClientConnection.nextMessageID(),
+ null,
+ ByteString.valueOf("cn=schema"),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ false,
+ LDAPFilter.decode("objectclass=ldapsubentry"),
+ attrList, 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);
+ 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
+ //OID is found in the result set or not.
+ List<String> syntaxList = new ArrayList<String>();
+ while(iter.hasNext())
+ {
+ AttributeValue val = iter.next();
+ //parse the OIDs.
+ syntaxList.add(getOIDFromLdapSyntax(val.toString()));
+ }
+
+ assertTrue(syntaxList.size() ==
+ DirectoryServer.getAttributeSyntaxSet().size() ) ;
+ //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();
+ }
+ }
+
+
+
+ /**
+ * Tests whether it is possible to add values after an umimplemented syntax
+ * has been subsitutited by DirectoryString syntax.
+ *
+ * @throws java.lang.Exception
+ */
+ public void testSubsitutionSyntaxAddValues() throws Exception
+ {
+ try
+ {
+ addSubtitutionSyntax();
+ //Add an attribute with undefined syntax.
+ int resultCode = TestCaseUtils.applyModifications(true,
+ "dn: cn=schema",
+ "changetype: modify",
+ "add: attributetypes",
+ "attributetypes: ( test-oid NAME 'test-attr' SYNTAX 9.9.9 )",
+ "-",
+ "add: objectclasses",
+ "objectclasses: ( oc-oid NAME 'testOC' SUP top AUXILIARY MUST test-attr)"
+ );
+ assertTrue(resultCode == 0);
+ TestCaseUtils.initializeTestBackend(true);
+
+ TestCaseUtils.addEntry(
+ "dn: cn=syntax-test,o=test",
+ "objectclass: person",
+ "objectclass: testOC",
+ "cn: syntax-test",
+ "sn: xyz",
+ "test-attr: test value for unimplemented syntax");
+ }
+ finally
+ {
+ deleteSubstitutionSyntax();
+ }
+ }
+
+
+ //Parses the OID from the syntax defitions.
+ private String getOIDFromLdapSyntax(String valueStr)
+ {
+ int pos = 0;
+ int length = valueStr.length();
+
+ while ((pos < length) && (valueStr.charAt(pos) == ' '))
+ {
+ pos++;
+ }
+ // The next character must be an open parenthesis. If it is not, then that
+ // is an error.
+ char c = valueStr.charAt(pos++);
+
+ // Skip over any spaces immediately following the opening parenthesis.
+ while ((pos < length) && ((c = valueStr.charAt(pos)) == ' '))
+ {
+ pos++;
+ }
+ 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);
+ }
+
+
+ //Adds a substitutionsyntax to the schema.
+ private void addSubtitutionSyntax() throws Exception
+ {
+ //Add the substitution syntax for an unimplemented syntax.
+ int resultCode = TestCaseUtils.applyModifications(true,
+ "dn: cn=schema",
+ "changetype: modify",
+ "add: ldapsyntaxes",
+ "ldapsyntaxes: ( 9.9.9 DESC 'Unimplemented Syntax' " +
+ " X-SUBST '1.3.6.1.4.1.1466.115.121.1.15' )");
+
+ assertTrue(resultCode==0);
+ }
+
+
+
+ //Deletes the substitutionSyntax from the schema.
+ private void deleteSubstitutionSyntax() throws Exception
+ {
+ //delete the substitution syntax.
+ int resultCode = TestCaseUtils.applyModifications(true,
+ "dn: cn=schema",
+ "changetype: modify",
+ "delete: ldapsyntaxes",
+ "ldapsyntaxes: ( 9.9.9 DESC 'Unimplemented Syntax' " +
+ " X-SUBST '1.3.6.1.4.1.1466.115.121.1.15' )");
+
+ assertTrue(resultCode==0);
+ }
}
--
Gitblit v1.10.0