opendj-server-legacy/src/main/java/org/opends/server/backends/SchemaBackend.java
@@ -64,6 +64,7 @@ import org.forgerock.opendj.ldap.schema.CoreSchema; import org.forgerock.opendj.ldap.schema.MatchingRule; import org.forgerock.opendj.ldap.schema.MatchingRuleUse; import org.forgerock.opendj.ldap.schema.NameForm; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.forgerock.opendj.ldap.schema.ObjectClassType; import org.forgerock.opendj.ldap.schema.SchemaElement; @@ -84,7 +85,6 @@ import org.opends.server.schema.DITContentRuleSyntax; import org.opends.server.schema.DITStructureRuleSyntax; import org.opends.server.schema.GeneralizedTimeSyntax; import org.opends.server.schema.NameFormSyntax; import org.opends.server.schema.ServerSchemaElement; import org.opends.server.schema.SomeSchemaElement; import org.opends.server.types.Attribute; @@ -104,7 +104,6 @@ import org.opends.server.types.LDIFImportConfig; import org.opends.server.types.LDIFImportResult; import org.opends.server.types.Modification; import org.opends.server.types.NameForm; import org.opends.server.types.Privilege; import org.opends.server.types.RestoreConfig; import org.opends.server.types.Schema; @@ -580,7 +579,7 @@ */ buildSchemaAttribute(schema.getSyntaxes(), userAttrs, operationalAttrs, ldapSyntaxesType, includeSchemaFile, false, true); buildSchemaAttribute(schema.getNameFormsByNameOrOID().values(), userAttrs, buildSchemaAttribute(schema.getNameForms(), userAttrs, operationalAttrs, nameFormsType, includeSchemaFile, false, true); buildSchemaAttribute(schema.getDITContentRules().values(), userAttrs, operationalAttrs, ditContentRulesType, includeSchemaFile, false, true); @@ -769,21 +768,7 @@ { for (ByteString v : a) { NameForm nf; try { nf = NameFormSyntax.decodeNameForm(v, newSchema, false); } catch (DirectoryException de) { logger.traceException(de); LocalizableMessage message = ERR_SCHEMA_MODIFY_CANNOT_DECODE_NAME_FORM.get( v, de.getMessageObject()); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, de); } NameForm nf = newSchema.parseNameForm(v.toString()); addNameForm(nf, newSchema, modifiedSchemaFiles); } } @@ -893,21 +878,7 @@ { for (ByteString v : a) { NameForm nf; try { nf = NameFormSyntax.decodeNameForm(v, newSchema, false); } catch (DirectoryException de) { logger.traceException(de); LocalizableMessage message = ERR_SCHEMA_MODIFY_CANNOT_DECODE_NAME_FORM.get( v, de.getMessageObject()); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, de); } NameForm nf = newSchema.parseNameForm(v.toString()); removeNameForm(nf, newSchema, mods, pos, modifiedSchemaFiles); } } @@ -1155,13 +1126,13 @@ // Otherwise, we're replacing an existing one. if (existingType.isPlaceHolder()) { String schemaFile = addNewSchemaElement(modifiedSchemaFiles, new SomeSchemaElement(attributeType)); String schemaFile = addNewSchemaElement(modifiedSchemaFiles, new ServerSchemaElement(attributeType)); schema.registerAttributeType(attributeType, schemaFile, false); } else { String schemaFile = replaceExistingSchemaElement( modifiedSchemaFiles, new SomeSchemaElement(attributeType), new SomeSchemaElement(existingType)); modifiedSchemaFiles, new ServerSchemaElement(attributeType), new ServerSchemaElement(existingType)); schema.replaceAttributeType(attributeType, existingType, schemaFile); } } @@ -1186,15 +1157,6 @@ modifiedSchemaFiles.add(schemaFile); } /** Update list of modified files and return the schema file to use for the added element (may be null). */ private String addNewSchemaElement(Set<String> modifiedSchemaFiles, SomeSchemaElement elem) { String schemaFile = elem.getSchemaFile(); String finalFile = schemaFile != null ? schemaFile : FILE_USER_SCHEMA_ELEMENTS; modifiedSchemaFiles.add(finalFile); return schemaFile == null ? finalFile : null; } /** * Update list of modified files and return the schema file to use for the * added element (may be null). @@ -1233,33 +1195,6 @@ } } /** Update list of modified files and return the schema file to use for the new element (may be null). */ private String replaceExistingSchemaElement( Set<String> modifiedSchemaFiles, SomeSchemaElement newElem, SomeSchemaElement existingElem) { String newSchemaFile = newElem.getSchemaFile(); String oldSchemaFile = existingElem.getSchemaFile(); if (newSchemaFile == null) { if (oldSchemaFile == null) { oldSchemaFile = FILE_USER_SCHEMA_ELEMENTS; } modifiedSchemaFiles.add(oldSchemaFile); return oldSchemaFile; } else if (oldSchemaFile == null || oldSchemaFile.equals(newSchemaFile)) { modifiedSchemaFiles.add(newSchemaFile); } else { modifiedSchemaFiles.add(newSchemaFile); modifiedSchemaFiles.add(oldSchemaFile); } return null; } /** * Update list of modified files and return the schema file to use for the new * element (may be null). @@ -1343,37 +1278,6 @@ } } // Make sure that the attribute type isn't used as a required or optional // attribute type in any objectclass. for (ObjectClass oc : schema.getObjectClasses()) { if (oc.getDeclaredRequiredAttributes().contains(removeType) || (oc.getDeclaredOptionalAttributes().contains(removeType) && !oc.isExtensible())) { LocalizableMessage message = ERR_SCHEMA_MODIFY_REMOVE_AT_IN_OC.get( removeType.getNameOrOID(), oc.getNameOrOID()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } } // Make sure that the attribute type isn't used as a required or optional // attribute type in any name form. for (List<NameForm> mappedForms : schema.getNameFormsByObjectClass().values()) { for(NameForm nf : mappedForms) { if (nf.getRequiredAttributes().contains(removeType) || nf.getOptionalAttributes().contains(removeType)) { LocalizableMessage message = ERR_SCHEMA_MODIFY_REMOVE_AT_IN_NF.get( removeType.getNameOrOID(), nf.getNameOrOID()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } } } // Make sure that the attribute type isn't used as a required, optional, or // prohibited attribute type in any DIT content rule. for (DITContentRule dcr : schema.getDITContentRules().values()) @@ -1402,7 +1306,7 @@ // If we've gotten here, then it's OK to remove the attribute type from // the schema. schema.deregisterAttributeType(removeType); String schemaFile = new SomeSchemaElement(removeType).getSchemaFile(); String schemaFile = new ServerSchemaElement(removeType).getSchemaFile(); if (schemaFile != null) { modifiedSchemaFiles.add(schemaFile); @@ -1513,13 +1417,13 @@ // Otherwise, we're replacing an existing one. if (existingClass.isPlaceHolder()) { String schemaFile = addNewSchemaElement(modifiedSchemaFiles, new SomeSchemaElement(objectClass)); String schemaFile = addNewSchemaElement(modifiedSchemaFiles, new ServerSchemaElement(objectClass)); schema.registerObjectClass(objectClass, schemaFile, false); } else { final String schemaFile = replaceExistingSchemaElement( modifiedSchemaFiles, new SomeSchemaElement(objectClass), new SomeSchemaElement(existingClass)); modifiedSchemaFiles, new ServerSchemaElement(objectClass), new ServerSchemaElement(existingClass)); schema.replaceObjectClass(objectClass, existingClass, schemaFile); } } @@ -1620,8 +1524,8 @@ // Make sure that the objectclass isn't used as the structural class for // any name form. List<NameForm> mappedForms = schema.getNameForm(removeClass); if (mappedForms != null) Collection<NameForm> mappedForms = schema.getNameForm(removeClass); if (!mappedForms.isEmpty()) { StringBuilder buffer = new StringBuilder(); for(NameForm nf : mappedForms) @@ -1677,52 +1581,9 @@ Set<String> modifiedSchemaFiles) throws DirectoryException { // First, see if the specified name form already exists. We'll check the // OID and all of the names, which means that it's possible there could be // more than one match (although if there is, then we'll refuse the // operation). NameForm existingNF = schema.getNameForm(nameForm.getOID()); for (String name : nameForm.getNames().keySet()) { NameForm nf = schema.getNameForm(name); if (nf == null) { continue; } else if (existingNF == null) { existingNF = nf; } else if (existingNF != nf) { // NOTE: We really do want to use "!=" instead of "! t.equals()" // because we want to check whether it's the same object instance, not // just a logical equivalent. LocalizableMessage message = ERR_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_NAME_FORM .get(nameForm.getNameOrOID(), existingNF.getNameOrOID(), nf.getNameOrOID()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } } // Make sure that the new name form doesn't reference an undefined // structural class, or an undefined required or optional attribute type, or // that any of them are marked OBSOLETE. // Make sure that the new name form doesn't reference an objectclass // or attributes that are marked OBSOLETE. ObjectClass structuralClass = nameForm.getStructuralClass(); if (! schema.hasObjectClass(structuralClass.getOID())) { LocalizableMessage message = ERR_SCHEMA_MODIFY_NF_UNDEFINED_STRUCTURAL_OC.get( nameForm.getNameOrOID(), structuralClass.getNameOrOID()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } if (structuralClass.getObjectClassType() != ObjectClassType.STRUCTURAL) { LocalizableMessage message = ERR_SCHEMA_MODIFY_NF_OC_NOT_STRUCTURAL.get( nameForm.getNameOrOID(), structuralClass.getNameOrOID()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } if (structuralClass.isObsolete()) { LocalizableMessage message = ERR_SCHEMA_MODIFY_NF_OC_OBSOLETE.get( @@ -1732,13 +1593,7 @@ for (AttributeType at : nameForm.getRequiredAttributes()) { if (! schema.hasAttributeType(at.getOID())) { LocalizableMessage message = ERR_SCHEMA_MODIFY_NF_UNDEFINED_REQUIRED_ATTR.get( nameForm.getNameOrOID(), at.getNameOrOID()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } else if (at.isObsolete()) if (at.isObsolete()) { LocalizableMessage message = ERR_SCHEMA_MODIFY_NF_OBSOLETE_REQUIRED_ATTR.get( nameForm.getNameOrOID(), at.getNameOrOID()); @@ -1748,13 +1603,7 @@ for (AttributeType at : nameForm.getOptionalAttributes()) { if (! schema.hasAttributeType(at.getOID())) { LocalizableMessage message = ERR_SCHEMA_MODIFY_NF_UNDEFINED_OPTIONAL_ATTR.get( nameForm.getNameOrOID(), at.getNameOrOID()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } else if (at.isObsolete()) if (at.isObsolete()) { LocalizableMessage message = ERR_SCHEMA_MODIFY_NF_OBSOLETE_OPTIONAL_ATTR.get( nameForm.getNameOrOID(), at.getNameOrOID()); @@ -1764,17 +1613,19 @@ // If there is no existing class, then we're adding a new name form. // Otherwise, we're replacing an existing one. if (existingNF == null) if (!schema.hasNameForm(nameForm.getNameOrOID())) { schema.registerNameForm(nameForm, false); addNewSchemaElement(modifiedSchemaFiles, nameForm); String schemaFile = addNewSchemaElement(modifiedSchemaFiles, new ServerSchemaElement(nameForm)); schema.registerNameForm(nameForm, schemaFile, false); } else { NameForm existingNF = schema.getNameForm(nameForm.getNameOrOID()); schema.deregisterNameForm(existingNF); schema.registerNameForm(nameForm, false); String schemaFile = replaceExistingSchemaElement( modifiedSchemaFiles, new ServerSchemaElement(nameForm), new ServerSchemaElement(existingNF)); schema.registerNameForm(nameForm, schemaFile, false); schema.rebuildDependentElements(existingNF); replaceExistingSchemaElement(modifiedSchemaFiles, nameForm, existingNF); } } @@ -1809,16 +1660,14 @@ Set<String> modifiedSchemaFiles) throws DirectoryException { // See if the specified name form is actually defined in the server schema. // If not, then fail. NameForm removeNF = schema.getNameForm(nameForm.getOID()); if (removeNF == null || !removeNF.equals(nameForm)) if (!schema.hasNameForm(nameForm.getOID())) { LocalizableMessage message = ERR_SCHEMA_MODIFY_REMOVE_NO_SUCH_NAME_FORM.get( nameForm.getNameOrOID()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, ERR_SCHEMA_MODIFY_REMOVE_NO_SUCH_NAME_FORM.get(nameForm.getNameOrOID())); } NameForm removeNF = schema.getNameForm(nameForm.getOID()); // See if there is another modification later to add the name form back // into the schema. If so, then it's a replace and we should ignore the // remove because adding it back will handle the replace. @@ -1838,7 +1687,7 @@ NameForm nf; try { nf = NameFormSyntax.decodeNameForm(v, schema, true); nf = schema.parseNameForm(v.toString()); } catch (DirectoryException de) { @@ -1859,8 +1708,7 @@ } } // Make sure that the name form isn't referenced by any DIT structure // rule. // Make sure that the name form isn't referenced by any DIT structure rule. DITStructureRule dsr = schema.getDITStructureRule(removeNF); if (dsr != null) { @@ -1869,8 +1717,7 @@ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } // If we've gotten here, then it's OK to remove the name form from the // schema. // If we've gotten here, then it's OK to remove the name form from the schema. schema.deregisterNameForm(removeNF); String schemaFile = getSchemaFile(removeNF); if (schemaFile != null) @@ -2635,7 +2482,7 @@ values = new LinkedHashSet<>(); for (AttributeType at : schema.getAttributeTypes()) { String atSchemaFile = new SomeSchemaElement(at).getSchemaFile(); String atSchemaFile = new ServerSchemaElement(at).getSchemaFile(); if (schemaFile.equals(atSchemaFile)) { addAttrTypeToSchemaFile(schema, schemaFile, at, values, addedTypes, 0); @@ -2674,14 +2521,11 @@ // is no hierarchical relationship between name forms, we don't need to // worry about ordering. values = new LinkedHashSet<>(); for (List<NameForm> forms : schema.getNameFormsByObjectClass().values()) for (NameForm nf : schema.getNameForms()) { for(NameForm nf : forms) if (schemaFile.equals(getSchemaFile(nf))) { if (schemaFile.equals(getSchemaFile(nf))) { values.add(ByteString.valueOfUtf8(nf.toString())); } values.add(ByteString.valueOfUtf8(nf.toString())); } } @@ -2811,7 +2655,7 @@ AttributeType superiorType = attributeType.getSuperiorType(); if (superiorType != null && schemaFile.equals(new SomeSchemaElement(attributeType).getSchemaFile()) && schemaFile.equals(new ServerSchemaElement(attributeType).getSchemaFile()) && !addedTypes.contains(superiorType)) { addAttrTypeToSchemaFile(schema, schemaFile, superiorType, values, @@ -3345,7 +3189,7 @@ { // Parse the attribute type. AttributeType attrType = schema.parseAttributeType(v.toString()); String schemaFile = new SomeSchemaElement(attrType).getSchemaFile(); String schemaFile = new ServerSchemaElement(attrType).getSchemaFile(); if (CONFIG_SCHEMA_ELEMENTS_FILE.equals(schemaFile)) { // Don't import the file containing the definitions of the opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
@@ -74,6 +74,7 @@ import org.forgerock.opendj.ldap.schema.CoreSchema; import org.forgerock.opendj.ldap.schema.MatchingRule; import org.forgerock.opendj.ldap.schema.MatchingRuleUse; import org.forgerock.opendj.ldap.schema.NameForm; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.forgerock.opendj.ldap.schema.Syntax; import org.forgerock.opendj.ldap.schema.UnknownSchemaElementException; @@ -160,7 +161,6 @@ import org.opends.server.types.LDIFImportConfig; import org.opends.server.types.LockManager; import org.opends.server.types.Modification; import org.opends.server.types.NameForm; import org.opends.server.types.Operation; import org.opends.server.types.Privilege; import org.opends.server.types.RestoreConfig; @@ -2676,31 +2676,19 @@ * @return The requested name forms, or {@code null} if no such name * form is defined in the schema. */ public static List<NameForm> getNameForm(ObjectClass objectClass) public static Collection<NameForm> getNameForm(ObjectClass objectClass) { return directoryServer.schema.getNameForm(objectClass); } /** * Retrieves the name form associated with the specified name or OID. * * @param lowerName The name or OID of the name form to retrieve, formatted * in all lowercase characters. * * @return The requested name form, or {@code null} if no such name form * is defined in the schema. */ public static NameForm getNameForm(String lowerName) { return directoryServer.schema.getNameForm(lowerName); } /** * Deregisters the provided name form with the Directory Server. * * @param nameForm The name form to deregister with the server. * @throws DirectoryException * If an error occurs. */ public static void deregisterNameForm(NameForm nameForm) public static void deregisterNameForm(NameForm nameForm) throws DirectoryException { directoryServer.schema.deregisterNameForm(nameForm); } opendj-server-legacy/src/main/java/org/opends/server/core/SchemaConfigManager.java
@@ -34,7 +34,6 @@ import org.forgerock.opendj.ldap.schema.Syntax; import org.opends.server.schema.DITContentRuleSyntax; import org.opends.server.schema.DITStructureRuleSyntax; import org.opends.server.schema.NameFormSyntax; import org.opends.server.types.Attribute; import org.opends.server.types.DITContentRule; import org.opends.server.types.DITStructureRule; @@ -43,7 +42,6 @@ import org.opends.server.types.InitializationException; import org.opends.server.types.LDIFImportConfig; import org.opends.server.types.Modification; import org.opends.server.types.NameForm; import org.opends.server.types.Schema; import org.opends.server.util.LDIFReader; import org.opends.server.util.StaticUtils; @@ -721,39 +719,12 @@ { for (ByteString v : a) { // Parse the name form. NameForm nf; try { nf = NameFormSyntax.decodeNameForm(v, schema, false); nf.getExtraProperties().remove(SCHEMA_PROPERTY_FILENAME); setSchemaFile(nf, schemaFile); } catch (DirectoryException de) { logger.traceException(de); LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_NAME_FORM.get( schemaFile, de.getMessageObject()); reportError(failOnError, de, message); continue; } catch (Exception e) { logger.traceException(e); LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_PARSE_NAME_FORM.get( schemaFile, v + ": " + getExceptionMessage(e)); reportError(failOnError, e, 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.registerNameForm(nf, failOnError); schema.registerNameForm(v.toString(), schemaFile, failOnError); } catch (DirectoryException de) { @@ -763,7 +734,7 @@ try { schema.registerNameForm(nf, true); schema.registerNameForm(v.toString(), schemaFile, true); } catch (Exception e) { opendj-server-legacy/src/main/java/org/opends/server/extensions/GoverningStructureRuleVirtualAttributeProvider.java
@@ -16,6 +16,7 @@ */ package org.opends.server.extensions; import java.util.Collection; import java.util.List; import org.forgerock.i18n.LocalizableMessage; @@ -25,6 +26,7 @@ import org.forgerock.opendj.ldap.RDN; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.NameForm; import org.forgerock.opendj.server.config.server.GoverningStructureRuleVirtualAttributeCfg; import org.opends.server.api.VirtualAttributeProvider; import org.opends.server.core.DirectoryServer; @@ -34,7 +36,6 @@ import org.opends.server.types.Attributes; import org.opends.server.types.DITStructureRule; import org.opends.server.types.Entry; import org.opends.server.types.NameForm; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.opends.server.types.VirtualAttributeRule; @@ -176,7 +177,7 @@ if (oc == null) { return null; } List<NameForm> listForms = DirectoryServer.getNameForm(oc); Collection<NameForm> listForms = DirectoryServer.getNameForm(oc); NameForm nameForm = null; DITStructureRule ditRule = null; //We iterate over all the nameforms while creating the entry and opendj-server-legacy/src/main/java/org/opends/server/schema/DITStructureRuleSyntax.java
@@ -27,13 +27,13 @@ import org.forgerock.i18n.LocalizableMessage; import org.forgerock.opendj.ldap.ByteSequence; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.schema.NameForm; import org.forgerock.opendj.ldap.schema.Syntax; import org.forgerock.opendj.server.config.server.AttributeSyntaxCfg; import org.opends.server.api.AttributeSyntax; import org.opends.server.core.DirectoryServer; import org.opends.server.types.DITStructureRule; import org.opends.server.types.DirectoryException; import org.opends.server.types.NameForm; import org.opends.server.types.Schema; /** @@ -318,12 +318,14 @@ pos = readWOID(lowerStr, woidBuffer, pos); nameFormGiven = true; nameForm = schema.getNameForm(woidBuffer.toString()); if (nameForm == null && ! allowUnknownElements) String nameOrOid = woidBuffer.toString(); if (!schema.hasNameForm(nameOrOid) && !allowUnknownElements) { throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, ERR_ATTR_SYNTAX_DSR_UNKNOWN_NAME_FORM.get(valueStr, woidBuffer)); } nameForm = schema.getNameForm(nameOrOid); } else if (lowerTokenName.equals("sup")) { opendj-server-legacy/src/main/java/org/opends/server/schema/NameFormSyntax.java
@@ -16,29 +16,11 @@ */ package org.opends.server.schema; import static org.opends.messages.SchemaMessages.*; import static org.opends.server.schema.SchemaConstants.*; import static org.opends.server.util.StaticUtils.*; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.LocalizableMessageDescriptor.Arg2; import org.forgerock.opendj.ldap.ByteSequence; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.forgerock.opendj.ldap.schema.ObjectClassType; import org.forgerock.opendj.ldap.schema.Syntax; import org.forgerock.opendj.server.config.server.AttributeSyntaxCfg; import org.opends.server.api.AttributeSyntax; import org.opends.server.core.DirectoryServer; import org.opends.server.types.DirectoryException; import org.opends.server.types.NameForm; import org.opends.server.types.Schema; /** * This class implements the name form description syntax, which is used to @@ -87,953 +69,5 @@ { return SYNTAX_NAME_FORM_DESCRIPTION; } /** * Decodes the contents of the provided ASN.1 octet string as a name form * definition according to the rules of this syntax. Note that the provided * octet string 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 ASN.1 octet string 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 structural objectclass and/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 name form definition. * * @throws DirectoryException If the provided value cannot be decoded as an * name form definition. */ public static NameForm decodeNameForm(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); // We'll do this a character at a time. First, skip over any leading whitespace. int pos = 0; int length = valueStr.length(); while (pos < length && valueStr.charAt(pos) == ' ') { pos++; } if (pos >= length) { // This means that the value was empty or contained only whitespace. That // is illegal. LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_EMPTY_VALUE.get(); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // The next character must be an open parenthesis. If it is not, then that // is an error. char c = valueStr.charAt(pos++); if (c != '(') { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_EXPECTED_OPEN_PARENTHESIS.get( valueStr, pos-1, c); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Skip over any spaces immediately following the opening parenthesis. while (pos < length && ((c = valueStr.charAt(pos)) == ' ')) { pos++; } if (pos >= length) { // This means that the end of the value was reached before we could find // the OID. Ths is illegal. LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // The next set of characters must be the OID. Strictly speaking, this // should only be a numeric OID, but we'll also allow for the // "ocname-oid" case as well. Look at the first character to figure out // which we will be using. 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++)) != ' ')) { if (c == '.') { if (lastWasPeriod) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_DOUBLE_PERIOD_IN_NUMERIC_OID. get(valueStr, pos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } else { lastWasPeriod = true; } } else if (! isDigit(c)) { // This must have been an illegal character. LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_ILLEGAL_CHAR_IN_NUMERIC_OID. get(valueStr, c, pos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } else { lastWasPeriod = false; } } } 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++)) != ' ')) { if (isAlpha(c) || isDigit(c) || c == '-' || (c == '_' && DirectoryServer.allowAttributeNameExceptions())) { // This is fine. It is an acceptable character. } else { // This must have been an illegal character. LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_ILLEGAL_CHAR_IN_STRING_OID. get(valueStr, 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 name form // description. Otherwise, parse out the OID. String oid; if (pos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } oid = lowerStr.substring(oidStartPos, pos-1); // Skip over the space(s) after the OID. while (pos < length && ((c = valueStr.charAt(pos)) == ' ')) { pos++; } if (pos >= length) { // This means that the end of the value was reached before we could find // the OID. Ths is illegal. LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // At this point, we should have a pretty specific syntax that describes // what may come next, but some of the components are optional and it would // be pretty easy to put something in the wrong order, so we will be very // flexible about what we can accept. Just look at the next token, figure // out what it is and how to treat what comes after it, then repeat until // we get to the end of the value. But before we start, set default values // for everything else we might need to know. LinkedHashMap<String,String> names = new LinkedHashMap<>(); String description = null; boolean isObsolete = false; ObjectClass structuralClass = null; LinkedHashSet<AttributeType> requiredAttributes = new LinkedHashSet<>(); LinkedHashSet<AttributeType> optionalAttributes = new LinkedHashSet<>(); LinkedHashMap<String,List<String>> extraProperties = new LinkedHashMap<>(); while (true) { StringBuilder tokenNameBuffer = new StringBuilder(); pos = readTokenName(valueStr, tokenNameBuffer, pos); String tokenName = tokenNameBuffer.toString(); String lowerTokenName = toLowerCase(tokenName); if (tokenName.equals(")")) { // We must be at the end of the value. If not, then that's a problem. if (pos < length) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_UNEXPECTED_CLOSE_PARENTHESIS. get(valueStr, pos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } break; } else if (lowerTokenName.equals("name")) { // This specifies the set of names for the name form. It may be a // single name in single quotes, or it may be an open parenthesis // followed by one or more names in single quotes separated by spaces. c = valueStr.charAt(pos++); if (c == '\'') { StringBuilder userBuffer = new StringBuilder(); StringBuilder lowerBuffer = new StringBuilder(); pos = readQuotedString(valueStr, lowerStr, userBuffer, lowerBuffer, pos-1); names.put(lowerBuffer.toString(), userBuffer.toString()); } else if (c == '(') { StringBuilder userBuffer = new StringBuilder(); StringBuilder lowerBuffer = new StringBuilder(); pos = readQuotedString(valueStr, lowerStr, userBuffer, lowerBuffer, pos); names.put(lowerBuffer.toString(), userBuffer.toString()); while (true) { if (valueStr.charAt(pos) == ')') { // Skip over any spaces after the parenthesis. pos++; while (pos < length && ((c = valueStr.charAt(pos)) == ' ')) { pos++; } break; } else { userBuffer = new StringBuilder(); lowerBuffer = new StringBuilder(); pos = readQuotedString(valueStr, lowerStr, userBuffer, lowerBuffer, pos); names.put(lowerBuffer.toString(), userBuffer.toString()); } } } else { // This is an illegal character. LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_ILLEGAL_CHAR.get(valueStr, c, pos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } else if (lowerTokenName.equals("desc")) { // This specifies the description for the name form. It is an // arbitrary string of characters enclosed in single quotes. StringBuilder descriptionBuffer = new StringBuilder(); pos = readQuotedString(valueStr, descriptionBuffer, pos); description = descriptionBuffer.toString(); } else if (lowerTokenName.equals("obsolete")) { // This indicates whether the name form should be considered obsolete. // We do not need to do any more parsing for this token. isObsolete = true; } else if (lowerTokenName.equals("oc")) { // This specifies the name or OID of the structural objectclass for this // name form. StringBuilder woidBuffer = new StringBuilder(); pos = readWOID(lowerStr, woidBuffer, pos); structuralClass = schema.getObjectClass(woidBuffer.toString()); if (structuralClass.isPlaceHolder()) { // This is bad because we don't know what the structural objectclass is. if (!allowUnknownElements) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_UNKNOWN_STRUCTURAL_CLASS.get(oid, woidBuffer); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); } } else if (structuralClass.getObjectClassType() != ObjectClassType.STRUCTURAL) { // This is bad because the associated structural class type is not // structural. LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_STRUCTURAL_CLASS_NOT_STRUCTURAL. get(oid, woidBuffer, structuralClass.getNameOrOID(), structuralClass.getObjectClassType()); throw new DirectoryException( ResultCode.CONSTRAINT_VIOLATION, message); } } else if (lowerTokenName.equals("must")) { LinkedList<AttributeType> attrs = new LinkedList<>(); // This specifies the set of required attributes for the name from. // It may be a single name or OID (not in quotes), or it may be an // open parenthesis followed by one or more names separated by spaces // and the dollar sign character, followed by a closing parenthesis. c = valueStr.charAt(pos++); if (c == '(') { while (true) { StringBuilder woidBuffer = new StringBuilder(); pos = readWOID(lowerStr, woidBuffer, pos); attrs.add(getAttributeType(schema, allowUnknownElements, oid, woidBuffer, ERR_ATTR_SYNTAX_NAME_FORM_UNKNOWN_REQUIRED_ATTR)); // The next character must be either a dollar sign or a closing parenthesis. c = valueStr.charAt(pos++); if (c == ')') { // This denotes the end of the list. break; } else if (c != '$') { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_ILLEGAL_CHAR.get( valueStr, c, pos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } } else { StringBuilder woidBuffer = new StringBuilder(); pos = readWOID(lowerStr, woidBuffer, pos-1); attrs.add(getAttributeType(schema, allowUnknownElements, oid, woidBuffer, ERR_ATTR_SYNTAX_NAME_FORM_UNKNOWN_REQUIRED_ATTR)); } requiredAttributes.addAll(attrs); } else if (lowerTokenName.equals("may")) { LinkedList<AttributeType> attrs = new LinkedList<>(); // This specifies the set of optional attributes for the name form. It // may be a single name or OID (not in quotes), or it may be an open // parenthesis followed by one or more names separated by spaces and the // dollar sign character, followed by a closing parenthesis. c = valueStr.charAt(pos++); if (c == '(') { while (true) { StringBuilder woidBuffer = new StringBuilder(); pos = readWOID(lowerStr, woidBuffer, pos); attrs.add(getAttributeType(schema, allowUnknownElements, oid, woidBuffer, ERR_ATTR_SYNTAX_NAME_FORM_UNKNOWN_OPTIONAL_ATTR)); // The next character must be either a dollar sign or a closing parenthesis. c = valueStr.charAt(pos++); if (c == ')') { // This denotes the end of the list. break; } else if (c != '$') { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_ILLEGAL_CHAR.get( valueStr, c, pos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } } else { StringBuilder woidBuffer = new StringBuilder(); pos = readWOID(lowerStr, woidBuffer, pos-1); attrs.add(getAttributeType(schema, allowUnknownElements, oid, woidBuffer, ERR_ATTR_SYNTAX_NAME_FORM_UNKNOWN_OPTIONAL_ATTR)); } optionalAttributes.addAll(attrs); } else { // This must be a non-standard property and it must be followed by // either a single value in single quotes or an open parenthesis // followed by one or more values in single quotes separated by spaces // followed by a close parenthesis. LinkedList<String> valueList = new LinkedList<>(); pos = readExtraParameterValues(valueStr, valueList, pos); extraProperties.put(tokenName, valueList); } } // Make sure that a structural class was specified. If not, then it cannot // be valid. if (structuralClass == null) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_NO_STRUCTURAL_CLASS.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } return new NameForm(value.toString(), names, oid, description, isObsolete, structuralClass, requiredAttributes, optionalAttributes, extraProperties); } private static AttributeType getAttributeType(Schema schema, boolean allowUnknownElements, String oid, StringBuilder woidBuffer, Arg2<Object, Object> msg) throws DirectoryException { String woidString = woidBuffer.toString(); AttributeType attr = schema.getAttributeType(woidString); if (attr.isPlaceHolder() && !allowUnknownElements) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, msg.get(oid, woidString)); } return attr; } /** * Reads the next token name from the name form definition, skipping over any * leading or trailing spaces, and appends it to the provided buffer. * * @param valueStr The string representation of the name form definition. * @param tokenName The buffer into which the token name will be written. * @param startPos The position in the provided string at which to start * reading the token name. * * @return The position of the first character that is not part of the token * name or one of the trailing spaces after it. * * @throws DirectoryException If a problem is encountered while reading the * token name. */ private static int readTokenName(String valueStr, StringBuilder tokenName, int startPos) throws DirectoryException { // Skip over any spaces at the beginning of the value. char c = '\u0000'; int length = valueStr.length(); while (startPos < length && ((c = valueStr.charAt(startPos)) == ' ')) { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Read until we find the next space. while (startPos < length && ((c = valueStr.charAt(startPos++)) != ' ')) { tokenName.append(c); } // Skip over any trailing spaces after the value. while (startPos < length && ((c = valueStr.charAt(startPos)) == ' ')) { startPos++; } // Return the position of the first non-space character after the token. return startPos; } /** * Reads the value of a string enclosed in single quotes, skipping over the * quotes and any leading or trailing spaces, and appending the string to the * provided buffer. * * @param valueStr The user-provided representation of the name form * definition. * @param valueBuffer The buffer into which the user-provided representation * of the value will be placed. * @param startPos The position in the provided string at which to start * reading the quoted string. * * @return The position of the first character that is not part of the quoted * string or one of the trailing spaces after it. * * @throws DirectoryException If a problem is encountered while reading the * quoted string. */ private static int readQuotedString(String valueStr, StringBuilder valueBuffer, int startPos) throws DirectoryException { // Skip over any spaces at the beginning of the value. char c = '\u0000'; int length = valueStr.length(); while (startPos < length && ((c = valueStr.charAt(startPos)) == ' ')) { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // The next character must be a single quote. if (c != '\'') { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_EXPECTED_QUOTE_AT_POS.get( valueStr, startPos, c); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Read until we find the closing quote. startPos++; while (startPos < length && ((c = valueStr.charAt(startPos)) != '\'')) { valueBuffer.append(c); startPos++; } // Skip over any trailing spaces after the value. startPos++; while (startPos < length && ((c = valueStr.charAt(startPos)) == ' ')) { startPos++; } // If we're at the end of the value, then that's illegal. if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Return the position of the first non-space character after the token. return startPos; } /** * Reads the value of a string enclosed in single quotes, skipping over the * quotes and any leading or trailing spaces, and appending the string to the * provided buffer. * * @param valueStr The user-provided representation of the name form * definition. * @param lowerStr The all-lowercase representation of the name form * definition. * @param userBuffer The buffer into which the user-provided representation * of the value will be placed. * @param lowerBuffer The buffer into which the all-lowercase representation * of the value will be placed. * @param startPos The position in the provided string at which to start * reading the quoted string. * * @return The position of the first character that is not part of the quoted * string or one of the trailing spaces after it. * * @throws DirectoryException If a problem is encountered while reading the * quoted string. */ private static int readQuotedString(String valueStr, String lowerStr, StringBuilder userBuffer, StringBuilder lowerBuffer, int startPos) throws DirectoryException { // Skip over any spaces at the beginning of the value. char c = '\u0000'; int length = lowerStr.length(); while (startPos < length && ((c = lowerStr.charAt(startPos)) == ' ')) { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(lowerStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // The next character must be a single quote. if (c != '\'') { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_EXPECTED_QUOTE_AT_POS.get( valueStr, startPos, c); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Read until we find the closing quote. startPos++; while (startPos < length && ((c = lowerStr.charAt(startPos)) != '\'')) { lowerBuffer.append(c); userBuffer.append(valueStr.charAt(startPos)); startPos++; } // Skip over any trailing spaces after the value. startPos++; while (startPos < length && ((c = lowerStr.charAt(startPos)) == ' ')) { startPos++; } // If we're at the end of the value, then that's illegal. if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(lowerStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Return the position of the first non-space character after the token. return startPos; } /** * Reads the attribute type description or numeric OID from the provided * string, skipping over any leading or trailing spaces, and appending the * value to the provided buffer. * * @param lowerStr The string from which the name or OID is to be read. * @param woidBuffer The buffer into which the name or OID should be * appended. * @param startPos The position at which to start reading. * * @return The position of the first character after the name or OID that is * not a space. * * @throws DirectoryException If a problem is encountered while reading the * name or OID. */ private static int readWOID(String lowerStr, StringBuilder woidBuffer, int startPos) throws DirectoryException { // Skip over any spaces at the beginning of the value. char c = '\u0000'; int length = lowerStr.length(); while (startPos < length && ((c = lowerStr.charAt(startPos)) == ' ')) { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(lowerStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // The next character must be either numeric (for an OID) or alphabetic (for // an attribute type description). 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 (startPos < length && ((c = lowerStr.charAt(startPos++)) != ' ')) { if (c == '.') { if (lastWasPeriod) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_DOUBLE_PERIOD_IN_NUMERIC_OID. get(lowerStr, startPos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } woidBuffer.append(c); lastWasPeriod = true; } else if (! isDigit(c)) { // Technically, this must be an illegal character. However, it is // possible that someone just got sloppy and did not include a space // between the name/OID and a closing parenthesis. In that case, // we'll assume it's the end of the value. What's more, we'll have // to prematurely return to nasty side effects from stripping off // additional characters. if (c == ')') { return startPos-1; } // This must have been an illegal character. LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_ILLEGAL_CHAR_IN_NUMERIC_OID. get(lowerStr, c, startPos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } else { woidBuffer.append(c); lastWasPeriod = false; } } } else if (isAlpha(c)) { // This must be an attribute type description. In this case, we will only // accept alphabetic characters, numeric digits, and the hyphen. while (startPos < length && ((c = lowerStr.charAt(startPos++)) != ' ')) { if (isAlpha(c) || isDigit(c) || c == '-' || (c == '_' && DirectoryServer.allowAttributeNameExceptions())) { woidBuffer.append(c); } else { // Technically, this must be an illegal character. However, it is // possible that someone just got sloppy and did not include a space // between the name/OID and a closing parenthesis. In that case, // we'll assume it's the end of the value. What's more, we'll have // to prematurely return to nasty side effects from stripping off // additional characters. if (c == ')') { return startPos-1; } // This must have been an illegal character. LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_ILLEGAL_CHAR_IN_STRING_OID. get(lowerStr, c, startPos-1); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } } else { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_ILLEGAL_CHAR.get(lowerStr, c, startPos); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Skip over any trailing spaces after the value. while (startPos < length && ((c = lowerStr.charAt(startPos)) == ' ')) { startPos++; } // If we're at the end of the value, then that's illegal. if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(lowerStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Return the position of the first non-space character after the token. return startPos; } /** * Reads the value for an "extra" parameter. It will handle a single unquoted * word (which is technically illegal, but we'll allow it), a single quoted * string, or an open parenthesis followed by a space-delimited set of quoted * strings or unquoted words followed by a close parenthesis. * * @param valueStr The string containing the information to be read. * @param valueList The list of "extra" parameter values read so far. * @param startPos The position in the value string at which to start * reading. * * @return The "extra" parameter value that was read. * * @throws DirectoryException If a problem occurs while attempting to read * the value. */ private static int readExtraParameterValues(String valueStr, List<String> valueList, int startPos) throws DirectoryException { // Skip over any leading spaces. int length = valueStr.length(); char c = '\u0000'; while (startPos < length && ((c = valueStr.charAt(startPos)) == ' ')) { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } // Look at the next character. If it is a quote, then parse until the next // quote and end. If it is an open parenthesis, then parse individual // values until the close parenthesis and end. Otherwise, parse until the // next space and end. if (c == '\'') { // Parse until the closing quote. StringBuilder valueBuffer = new StringBuilder(); startPos++; while (startPos < length && ((c = valueStr.charAt(startPos)) != '\'')) { valueBuffer.append(c); startPos++; } startPos++; valueList.add(valueBuffer.toString()); } else if (c == '(') { startPos++; // We're expecting a list of values. Quoted, space separated. while (true) { // Skip over any leading spaces; while (startPos < length && ((c = valueStr.charAt(startPos)) == ' ')) { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } if (c == ')') { // This is the end of the list. startPos++; break; } else if (c == '(') { // This is an illegal character. LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_ILLEGAL_CHAR.get( valueStr, c, startPos); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } else if (c == '\'') { // We have a quoted string StringBuilder valueBuffer = new StringBuilder(); startPos++; while (startPos < length && ((c = valueStr.charAt(startPos)) != '\'')) { valueBuffer.append(c); startPos++; } valueList.add(valueBuffer.toString()); startPos++; } else { //Consider unquoted string StringBuilder valueBuffer = new StringBuilder(); while (startPos < length && ((c = valueStr.charAt(startPos)) != ' ')) { valueBuffer.append(c); startPos++; } valueList.add(valueBuffer.toString()); } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } } else { // Parse until the next space. StringBuilder valueBuffer = new StringBuilder(); while (startPos < length && ((c = valueStr.charAt(startPos)) != ' ')) { valueBuffer.append(c); startPos++; } valueList.add(valueBuffer.toString()); } // Skip over any trailing spaces. while (startPos < length && valueStr.charAt(startPos) == ' ') { startPos++; } if (startPos >= length) { LocalizableMessage message = ERR_ATTR_SYNTAX_NAME_FORM_TRUNCATED_VALUE.get(valueStr); throw new DirectoryException( ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } return startPos; } } opendj-server-legacy/src/main/java/org/opends/server/types/DITStructureRule.java
@@ -23,6 +23,7 @@ import java.util.Set; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.forgerock.opendj.ldap.schema.NameForm; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.forgerock.opendj.ldap.schema.SchemaElement; opendj-server-legacy/src/main/java/org/opends/server/types/DirectoryConfig.java
@@ -17,7 +17,6 @@ package org.opends.server.types; import java.util.Collection; import java.util.List; import java.util.Set; import org.forgerock.i18n.LocalizableMessage; @@ -26,6 +25,7 @@ import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.MatchingRule; import org.forgerock.opendj.ldap.schema.MatchingRuleUse; import org.forgerock.opendj.ldap.schema.NameForm; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.forgerock.opendj.ldap.schema.Syntax; import org.opends.server.api.AlertGenerator; @@ -397,36 +397,21 @@ } /** * Retrieves the list of name forms associated with the specified * Retrieves the collection of name forms associated with the specified * structural objectclass. * * @param objectClass The structural objectclass for which to * retrieve the associated name form. * * @return The list of requested name forms, or <CODE>null</CODE> * @return The collection of requested name forms, or <CODE>null</CODE> * if no such name form is defined in the schema. */ public static List<NameForm> getNameForm(ObjectClass objectClass) public static Collection<NameForm> getNameForm(ObjectClass objectClass) { return DirectoryServer.getNameForm(objectClass); } /** * Retrieves the name form associated with the specified name or * OID. * * @param lowerName The name or OID of the name form to retrieve, * formatted in all lowercase characters. * * @return The requested name form, or <CODE>null</CODE> if no such * name form is defined in the schema. */ public static NameForm getNameForm(String lowerName) { return DirectoryServer.getNameForm(lowerName); } /** * Registers the provided alert generator with the Directory Server. * * @param alertGenerator The alert generator to register. opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java
@@ -47,6 +47,7 @@ import org.forgerock.opendj.ldap.SearchScope; import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.MatchingRule; import org.forgerock.opendj.ldap.schema.NameForm; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.forgerock.opendj.ldap.schema.ObjectClassType; import org.opends.server.api.CompressedSchema; @@ -1614,9 +1615,10 @@ * DITStructureRules corresponding to other non-acceptable * nameforms are not applied. */ List<NameForm> listForms = DirectoryServer.getNameForm(structuralClass); if(listForms != null) Collection<NameForm> forms = DirectoryServer.getNameForm(structuralClass); if (forms != null) { List<NameForm> listForms = new ArrayList<NameForm>(forms); boolean matchFound = false; boolean obsolete = true; for(int index=0; index <listForms.size(); index++) @@ -2158,8 +2160,7 @@ } else { List<NameForm> allNFs = DirectoryServer.getNameForm(parentStructuralClass); Collection<NameForm> allNFs = DirectoryServer.getNameForm(parentStructuralClass); if(allNFs != null) { for(NameForm parentNF : allNFs) opendj-server-legacy/src/main/java/org/opends/server/types/NameForm.java
File was deleted opendj-server-legacy/src/main/java/org/opends/server/types/Schema.java
@@ -24,7 +24,6 @@ import java.io.FilenameFilter; import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -53,6 +52,7 @@ import org.forgerock.opendj.ldap.schema.MatchingRule; import org.forgerock.opendj.ldap.schema.MatchingRuleUse; import org.forgerock.opendj.ldap.schema.MatchingRuleUse.Builder; import org.forgerock.opendj.ldap.schema.NameForm; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.forgerock.opendj.ldap.schema.SchemaBuilder; import org.forgerock.opendj.ldap.schema.SchemaElement; @@ -64,7 +64,6 @@ import org.opends.server.core.SchemaConfigManager; import org.opends.server.schema.DITContentRuleSyntax; import org.opends.server.schema.DITStructureRuleSyntax; import org.opends.server.schema.NameFormSyntax; import org.opends.server.util.Base64; import org.opends.server.util.ServerConstants; import org.opends.server.util.StaticUtils; @@ -132,19 +131,6 @@ ditStructureRulesByNameForm; /** * The set of name forms for this schema, mapped between the structural * objectclass for the definition and the list of name forms. */ private ConcurrentHashMap<ObjectClass,List<NameForm>> nameFormsByOC; /** * The set of name forms for this schema, mapped between the 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. */ @@ -192,8 +178,6 @@ ditContentRules = new ConcurrentHashMap<ObjectClass,DITContentRule>(); ditStructureRulesByID = new ConcurrentHashMap<Integer,DITStructureRule>(); ditStructureRulesByNameForm = new ConcurrentHashMap<NameForm,DITStructureRule>(); nameFormsByOC = new ConcurrentHashMap<ObjectClass,List<NameForm>>(); nameFormsByName = new ConcurrentHashMap<String,NameForm>(); ldapSyntaxDescriptions = new ConcurrentHashMap<String,LDAPSyntaxDescription>(); subordinateTypes = new ConcurrentHashMap<AttributeType,List<AttributeType>>(); @@ -369,6 +353,36 @@ } /** * Parses a name form from its provided definition. * * @param definition * The definition of the name form * @return the name form * @throws DirectoryException * If an error occurs */ public NameForm parseNameForm(final String definition) throws DirectoryException { try { SchemaBuilder builder = new SchemaBuilder(schemaNG); builder.addNameForm(definition, true); org.forgerock.opendj.ldap.schema.Schema newSchema = builder.toSchema(); rejectSchemaWithWarnings(newSchema); return newSchema.getNameForm(parseNameFormOID(definition)); } catch (UnknownSchemaElementException e) { LocalizableMessage msg = ERR_NAME_FORM_CANNOT_REGISTER.get(definition); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, msg, e); } catch (LocalizedIllegalArgumentException e) { throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, e.getMessageObject(), e); } } /** * Registers a list of attribute types from their provided definitions. * <p> * This method allows to do only one schema change for multiple definitions, @@ -572,6 +586,11 @@ return parseOID(definition, ResultCode.INVALID_ATTRIBUTE_SYNTAX, ERR_PARSING_MATCHING_RULE_USE_OID); } private String parseNameFormOID(String definition) throws DirectoryException { return parseOID(definition, ResultCode.INVALID_ATTRIBUTE_SYNTAX, ERR_PARSING_NAME_FORM_OID); } /** * Returns the OID from the provided schema element definition, assuming the * definition is valid. @@ -832,7 +851,8 @@ ObjectClass.Builder b = builder.buildObjectClass(objectClass); if (schemaFile != null) { b.removeExtraProperty(SCHEMA_PROPERTY_FILENAME).extraProperties(SCHEMA_PROPERTY_FILENAME, schemaFile); b.removeExtraProperty(SCHEMA_PROPERTY_FILENAME) .extraProperties(SCHEMA_PROPERTY_FILENAME, schemaFile); } if (overwriteExisting) { @@ -1677,36 +1697,15 @@ /** * Retrieves the name form definitions for this schema, as a mapping * between the objectclass for the name forms and the name forms * themselves. * Retrieves the name form definitions for this schema. * * @return The name form definitions for this schema. */ public ConcurrentHashMap<ObjectClass,List<NameForm>> getNameFormsByObjectClass() public Collection<NameForm> getNameForms() { return nameFormsByOC; return schemaNG.getNameForms(); } /** * Retrieves the name form definitions for this schema, as a mapping * between the names/OID for the name form and the name form itself. * Each name form may be present multiple times with different names * and its OID. The contents of the returned mapping must not be * altered. * * @return The name form definitions for this schema. */ public ConcurrentHashMap<String,NameForm> getNameFormsByNameOrOID() { return nameFormsByName; } /** * Indicates whether this schema definition includes a name form * with the specified name or OID. @@ -1720,7 +1719,7 @@ */ public boolean hasNameForm(String lowerName) { return nameFormsByName.containsKey(lowerName); return schemaNG.hasNameForm(lowerName); } @@ -1736,9 +1735,9 @@ * name forms are registered with the provided * objectClass. */ public List<NameForm> getNameForm(ObjectClass objectClass) public Collection<NameForm> getNameForm(ObjectClass objectClass) { return nameFormsByOC.get(objectClass); return schemaNG.getNameForms(objectClass); } @@ -1754,7 +1753,7 @@ */ public NameForm getNameForm(String lowerName) { return nameFormsByName.get(lowerName); return schemaNG.getNameForm(lowerName); } @@ -1762,116 +1761,95 @@ /** * Registers the provided name form definition with this schema. * * @param nameForm The name form definition to register. * @param overwriteExisting Indicates whether to overwrite an * existing mapping if there are any * conflicts (i.e., another name form * with the same objectclass). * * @throws DirectoryException If a conflict is encountered and the * <CODE>overwriteExisting</CODE> flag * is set to <CODE>false</CODE> * @param nameForm * The name form definition to register. * @param schemaFile * The schema file where this definition belongs, maybe {@code null} * @param overwriteExisting * Indicates whether to overwrite an existing mapping if there are any conflicts (i.e., * another name form with the same objectclass). * @throws DirectoryException * If a conflict is encountered and the <CODE>overwriteExisting</CODE> flag is set to * <CODE>false</CODE> */ public void registerNameForm(NameForm nameForm, boolean overwriteExisting) throws DirectoryException public void registerNameForm(NameForm nameForm, String schemaFile, boolean overwriteExisting) throws DirectoryException { synchronized (nameFormsByOC) exclusiveLock.lock(); try { ObjectClass objectClass = nameForm.getStructuralClass(); List<NameForm> mappedForms = nameFormsByOC.get(objectClass); if (! overwriteExisting) SchemaBuilder builder = new SchemaBuilder(schemaNG); NameForm.Builder formBuilder = builder.buildNameForm(nameForm); if (schemaFile != null) { if(mappedForms !=null) { //Iterate over the forms to make sure we aren't adding a //duplicate. for(NameForm nf : mappedForms) { if(nf.equals(nameForm)) { LocalizableMessage message = ERR_SCHEMA_CONFLICTING_NAME_FORM_OC. get(nameForm.getNameOrOID(), objectClass.getNameOrOID(), nf.getNameOrOID()); throw new DirectoryException( ResultCode.CONSTRAINT_VIOLATION, message); } } } String oid = toLowerCase(nameForm.getOID()); if (nameFormsByName.containsKey(oid)) { NameForm conflictingNameForm = nameFormsByName.get(oid); LocalizableMessage message = ERR_SCHEMA_CONFLICTING_NAME_FORM_OID. get(nameForm.getNameOrOID(), oid, conflictingNameForm.getNameOrOID()); throw new DirectoryException( ResultCode.CONSTRAINT_VIOLATION, message); } for (String name : nameForm.getNames().keySet()) { if (nameFormsByName.containsKey(name)) { NameForm conflictingNameForm = nameFormsByName.get(name); LocalizableMessage message = ERR_SCHEMA_CONFLICTING_NAME_FORM_NAME. get(nameForm.getNameOrOID(), oid, conflictingNameForm.getNameOrOID()); throw new DirectoryException( ResultCode.CONSTRAINT_VIOLATION, message); } } formBuilder.removeExtraProperty(SCHEMA_PROPERTY_FILENAME) .extraProperties(SCHEMA_PROPERTY_FILENAME, schemaFile); } if(mappedForms == null) if (overwriteExisting) { mappedForms = new ArrayList<>(); formBuilder.addToSchemaOverwrite(); } mappedForms.add(nameForm); nameFormsByOC.put(objectClass, mappedForms); nameFormsByName.put(toLowerCase(nameForm.getOID()), nameForm); for (String name : nameForm.getNames().keySet()) else { nameFormsByName.put(name, nameForm); formBuilder.addToSchema(); } switchSchema(builder.toSchema()); } finally { exclusiveLock.unlock(); } } /** * Registers the provided name form definition with this schema. * * @param definition * The name form definition to register. * @param schemaFile * The schema file where this definition belongs, maybe {@code null} * @param overwriteExisting * Indicates whether to overwrite an existing mapping if there are any conflicts * @throws DirectoryException * If a conflict is encountered and the <CODE>overwriteExisting</CODE> flag is set to * <CODE>false</CODE> */ public void registerNameForm(String definition, String schemaFile, boolean overwriteExisting) throws DirectoryException { exclusiveLock.lock(); try { String definitionWithFile = getDefinitionWithSchemaFile(definition, schemaFile); switchSchema(new SchemaBuilder(schemaNG) .addNameForm(definitionWithFile, overwriteExisting) .toSchema()); } finally { exclusiveLock.unlock(); } } /** * Deregisters the provided name form definition with this schema. * * @param nameForm The name form definition to deregister. * @throws DirectoryException * If an error occurs. */ public void deregisterNameForm(NameForm nameForm) public void deregisterNameForm(NameForm nameForm) throws DirectoryException { synchronized (nameFormsByOC) exclusiveLock.lock(); try { List<NameForm> mappedForms = nameFormsByOC.get( nameForm.getStructuralClass()); if(mappedForms != null) { mappedForms.remove(nameForm); if(mappedForms.isEmpty()) { nameFormsByOC.remove(nameForm.getStructuralClass()); } } nameFormsByOC.remove(nameForm.getStructuralClass()); nameFormsByName.remove(toLowerCase(nameForm.getOID()), nameForm); for (String name : nameForm.getNames().keySet()) { nameFormsByName.remove(name, nameForm); } SchemaBuilder builder = new SchemaBuilder(schemaNG); builder.removeNameForm(nameForm.getNameOrOID()); switchSchema(builder.toSchema()); } finally { exclusiveLock.unlock(); } } @@ -2053,17 +2031,13 @@ } } for (List<NameForm> mappedForms : nameFormsByOC.values()) for (NameForm nameForm : getNameForms()) { for (NameForm nf : mappedForms) if (nameForm.getRequiredAttributes().contains(type) || nameForm.getOptionalAttributes().contains(type)) { if (nf.getRequiredAttributes().contains(type) || nf.getOptionalAttributes().contains(type)) { NameForm newNF = recreateFromDefinition(nf); deregisterNameForm(nf); registerNameForm(newNF, true); rebuildDependentElements(nf, depth + 1); } deregisterNameForm(nameForm); registerNameForm(nameForm.toString(), getSchemaFileName(nameForm), true); rebuildDependentElements(nameForm, depth + 1); } } @@ -2111,16 +2085,15 @@ { circularityCheck(depth, c); List<NameForm> mappedForms = nameFormsByOC.get(c); Collection<NameForm> mappedForms = getNameForm(c); if (mappedForms != null) { for (NameForm nf : mappedForms) { if (nf != null) { NameForm newNF = recreateFromDefinition(nf); deregisterNameForm(nf); registerNameForm(newNF, true); registerNameForm(nf.toString(), getSchemaFileName(nf), true); rebuildDependentElements(nf, depth + 1); } } @@ -2175,8 +2148,7 @@ throws DirectoryException { ByteString value = ByteString.valueOfUtf8(dcr.toString()); DITContentRule copy = DITContentRuleSyntax.decodeDITContentRule(value, this, false); DITContentRule copy = DITContentRuleSyntax.decodeDITContentRule(value, this, false); setSchemaFile(copy, getSchemaFile(dcr)); return copy; } @@ -2191,15 +2163,6 @@ return copy; } private NameForm recreateFromDefinition(NameForm nf) throws DirectoryException { ByteString value = ByteString.valueOfUtf8(nf.toString()); NameForm copy = NameFormSyntax.decodeNameForm(value, this, false); setSchemaFile(copy, getSchemaFile(nf)); return copy; } /** * Creates a new {@link Schema} object that is a duplicate of this one. It elements may be added * and removed from the duplicate without impacting this version. @@ -2223,8 +2186,6 @@ dupSchema.ditContentRules.putAll(ditContentRules); dupSchema.ditStructureRulesByID.putAll(ditStructureRulesByID); dupSchema.ditStructureRulesByNameForm.putAll(ditStructureRulesByNameForm); dupSchema.nameFormsByOC.putAll(nameFormsByOC); dupSchema.nameFormsByName.putAll(nameFormsByName); dupSchema.ldapSyntaxDescriptions.putAll(ldapSyntaxDescriptions); dupSchema.oldestModificationTime = oldestModificationTime; dupSchema.youngestModificationTime = youngestModificationTime; @@ -2671,18 +2632,6 @@ ditStructureRulesByNameForm = null; } if (nameFormsByName != null) { nameFormsByName.clear(); nameFormsByName = null; } if (nameFormsByOC != null) { nameFormsByOC.clear(); nameFormsByOC = null; } if (subordinateTypes != null) { subordinateTypes.clear(); opendj-server-legacy/src/messages/org/opends/messages/schema.properties
@@ -510,3 +510,9 @@ of ldap syntax: '%s' ERR_PARSING_MATCHING_RULE_USE_OID_347=Unable to parse the OID from the provided definition \ of matching rule use: '%s' ERR_DIT_CONTENT_RULE_CANNOT_REGISTER_348=DIT content rule could not be \ registered from definition: %s ERR_NAME_FORM_CANNOT_REGISTER_349=Name form could not be \ registered from definition: %s ERR_PARSING_NAME_FORM_OID_350=Unable to parse the OID from the provided definition \ of name form: '%s' opendj-server-legacy/src/test/java/org/opends/server/backends/SchemaBackendTestCase.java
@@ -1101,7 +1101,7 @@ assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName)); runModify(argsNotPermissive(), ldif, SUCCESS); runModify(argsNotPermissive(), ldif1, UNWILLING_TO_PERFORM); runModify(argsNotPermissive(), ldif1, CONSTRAINT_VIOLATION); assertTrue(DirectoryServer.getSchema().hasAttributeType(attrName)); } finally @@ -1928,7 +1928,7 @@ String nameFormName = "testaddnameformwithundefinedreqat"; assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName)); runModify(argsNotPermissive(), ldif, INVALID_ATTRIBUTE_SYNTAX); runModify(argsNotPermissive(), ldif, CONSTRAINT_VIOLATION); assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName)); } @@ -1961,7 +1961,7 @@ String nameFormName = "testaddnameformwithmultipleundefinedreqat"; assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName)); runModify(argsNotPermissive(), ldif, INVALID_ATTRIBUTE_SYNTAX); runModify(argsNotPermissive(), ldif, CONSTRAINT_VIOLATION); assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName)); } @@ -1993,7 +1993,7 @@ String nameFormName = "testaddnameformwithundefinedoptat"; assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName)); runModify(argsNotPermissive(), ldif, INVALID_ATTRIBUTE_SYNTAX); runModify(argsNotPermissive(), ldif, CONSTRAINT_VIOLATION); assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName)); } @@ -2026,7 +2026,7 @@ String nameFormName = "testaddnameformwithmultipleundefinedoptat"; assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName)); runModify(argsNotPermissive(), ldif, INVALID_ATTRIBUTE_SYNTAX); runModify(argsNotPermissive(), ldif, CONSTRAINT_VIOLATION); assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName)); } @@ -2051,7 +2051,7 @@ String nameFormName = "testaddnameformwithundefinedoc"; assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName)); runModify(argsNotPermissive(), ldif, INVALID_ATTRIBUTE_SYNTAX); runModify(argsNotPermissive(), ldif, CONSTRAINT_VIOLATION); assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName)); } @@ -2082,7 +2082,7 @@ String nameFormName = "testaddnameformwithauxiliaryoc"; assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName)); runModify(argsNotPermissive(), ldif, INVALID_ATTRIBUTE_SYNTAX); runModify(argsNotPermissive(), ldif, CONSTRAINT_VIOLATION); assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName)); } opendj-server-legacy/src/test/java/org/opends/server/schema/GenericSchemaTestCase.java
@@ -25,13 +25,13 @@ import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.MatchingRule; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.forgerock.opendj.ldap.schema.NameForm; import org.forgerock.opendj.ldap.schema.Syntax; import org.opends.server.TestCaseUtils; import org.opends.server.core.DirectoryServer; import org.opends.server.types.Attribute; import org.opends.server.types.Entry; import org.opends.server.types.LDIFImportConfig; import org.opends.server.types.NameForm; import org.opends.server.types.Schema; import org.opends.server.util.LDIFReader; import org.testng.annotations.BeforeClass; @@ -318,8 +318,7 @@ { for (ByteString v : a) { NameForm nf = NameFormSyntax.decodeNameForm( v, DirectoryServer.getSchema(), true); NameForm nf = DirectoryServer.getSchema().parseNameForm(v.toString()); if (! isNumericOID(nf.getOID())) { invalidOIDs.add(nf.getNameOrOID());