/*
* 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 2006-2008 Sun Microsystems, Inc.
* Portions copyright 2013 ForgeRock AS.
*/
package org.opends.server.types;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.opends.server.schema.DITContentRuleSyntax;
import static org.opends.server.loggers.debug.DebugLogger.*;
import org.opends.server.loggers.debug.DebugTracer;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.Validator.*;
/**
* This class defines a DIT content rule, which defines the set of
* allowed, required, and prohibited attributes for entries with a
* given structural objectclass, and also indicates which auxiliary
* classes that may be included in the entry.
*/
@org.opends.server.types.PublicAPI(
stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
mayInstantiate=false,
mayExtend=false,
mayInvoke=true)
public final class DITContentRule
implements SchemaFileElement
{
/**
* The tracer object for the debug logger.
*/
private static final DebugTracer TRACER = getTracer();
// Indicates whether this content rule is declared "obsolete".
private final boolean isObsolete;
// The set of additional name-value pairs associated with this
// content rule definition.
private final Map<String,List<String>> extraProperties;
// The set of names for this DIT content rule, in a mapping between
// the all-lowercase form and the user-defined form.
private final Map<String,String> names;
// The structural objectclass for this DIT content rule.
private final ObjectClass structuralClass;
// The set of auxiliary objectclasses that entries with this content
// rule may contain, in a mapping between the objectclass and the
// user-defined name for that class.
private final Set<ObjectClass> auxiliaryClasses;
// The set of optional attribute types for this DIT content rule.
private final Set<AttributeType> optionalAttributes;
// The set of prohibited attribute types for this DIT content rule.
private final Set<AttributeType> prohibitedAttributes;
// The set of required attribute types for this DIT content rule.
private final Set<AttributeType> requiredAttributes;
// The definition string used to create this DIT content rule.
private final String definition;
// The description for this DIT content rule.
private final String description;
/**
* Creates a new DIT content rule definition with the provided
* information.
*
* @param definition The definition string used to
* create this DIT content rule. It
* must not be {@code null}.
* @param structuralClass The structural objectclass for this
* DIT content rule. It must not be
* {@code null}.
* @param names The set of names that may be used
* to reference this DIT content rule.
* @param description The description for this DIT
* content rule.
* @param auxiliaryClasses The set of auxiliary classes for
* this DIT content rule
* @param requiredAttributes The set of required attribute types
* for this DIT content rule.
* @param optionalAttributes The set of optional attribute types
* for this DIT content rule.
* @param prohibitedAttributes The set of prohibited attribute
* types for this DIT content rule.
* @param isObsolete Indicates whether this DIT content
* rule is declared "obsolete".
* @param extraProperties A set of extra properties for this
* DIT content rule.
*/
public DITContentRule(String definition,
ObjectClass structuralClass,
Map<String,String> names, String description,
Set<ObjectClass> auxiliaryClasses,
Set<AttributeType> requiredAttributes,
Set<AttributeType> optionalAttributes,
Set<AttributeType> prohibitedAttributes,
boolean isObsolete,
Map<String,List<String>> extraProperties)
{
ensureNotNull(definition, structuralClass);
this.structuralClass = structuralClass;
this.description = description;
this.isObsolete = isObsolete;
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)
{
if (debugEnabled())
{
TRACER.debugCaught(DebugLogLevel.ERROR, e);
}
defStr = definition;
}
this.definition = defStr;
}
else
{
this.definition = definition;
}
if ((names == null) || names.isEmpty())
{
this.names = new LinkedHashMap<String,String>(0);
}
else
{
this.names = new LinkedHashMap<String,String>(names);
}
if ((auxiliaryClasses == null) || auxiliaryClasses.isEmpty())
{
this.auxiliaryClasses = new LinkedHashSet<ObjectClass>(0);
}
else
{
this.auxiliaryClasses =
new LinkedHashSet<ObjectClass>(auxiliaryClasses);
}
if ((requiredAttributes == null) || requiredAttributes.isEmpty())
{
this.requiredAttributes = new LinkedHashSet<AttributeType>(0);
}
else
{
this.requiredAttributes =
new LinkedHashSet<AttributeType>(requiredAttributes);
}
if ((optionalAttributes == null) || optionalAttributes.isEmpty())
{
this.optionalAttributes = new LinkedHashSet<AttributeType>(0);
}
else
{
this.optionalAttributes =
new LinkedHashSet<AttributeType>(optionalAttributes);
}
if ((prohibitedAttributes == null) ||
prohibitedAttributes.isEmpty())
{
this.prohibitedAttributes = new LinkedHashSet<AttributeType>(0);
}
else
{
this.prohibitedAttributes =
new LinkedHashSet<AttributeType>(prohibitedAttributes);
}
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 DIT content
* rule.
*
* @return The definition string used to create this DIT content
* rule.
*/
public String getDefinition()
{
return definition;
}
/**
* {@inheritDoc}
*/
public DITContentRule recreateFromDefinition(Schema schema)
throws DirectoryException
{
ByteString value = ByteString.valueOf(definition);
DITContentRule dcr =
DITContentRuleSyntax.decodeDITContentRule(value, schema,
false);
dcr.setSchemaFile(getSchemaFile());
return dcr;
}
/**
* Retrieves the structural objectclass for this DIT content rule.
*
* @return The structural objectclass for this DIT content rule.
*/
public ObjectClass getStructuralClass()
{
return structuralClass;
}
/**
* Retrieves the set of names that may be used to reference this DIT
* content rule. The returned object will be a mapping between each
* name in all lowercase characters and that name in a user-defined
* form (which may include mixed capitalization).
*
* @return The set of names that may be used to reference this DIT
* content rule.
*/
public Map<String,String> getNames()
{
return names;
}
/**
* Retrieves the primary name to use to reference this DIT content
* rule.
*
* @return The primary name to use to reference this DIT content
* rule, or {@code null} if there is none.
*/
public String getName()
{
if (names.isEmpty())
{
return null;
}
else
{
return names.values().iterator().next();
}
}
/**
* Indicates whether the provided lowercase name may be used to
* reference this DIT content rule.
*
* @param lowerName The name for which to make the determination,
* in all lowercase characters.
*
* @return {@code true} if the provided lowercase name may be used
* to reference this DIT content rule, or {@code false} if
* not.
*/
public boolean hasName(String lowerName)
{
return names.containsKey(lowerName);
}
/**
* Retrieves the name of the schema file that contains the
* definition for this DIT content rule.
*
* @return The name of the schema file that contains the definition
* for this DIT content rule, 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 name of the schema file that contains the
* definition for this DIT content rule.
*
* @param schemaFile The name of the schema file that contains the
* definition for this DIT content rule.
*/
public void setSchemaFile(String schemaFile)
{
setExtraProperty(SCHEMA_PROPERTY_FILENAME, schemaFile);
}
/**
* Retrieves the description for this DIT content rule.
*
* @return The description for this DIT content rule, or
* {@code null} if there is none.
*/
public String getDescription()
{
return description;
}
/**
* Retrieves the set of auxiliary objectclasses that may be used for
* entries associated with this DIT content rule.
*
* @return The set of auxiliary objectclasses that may be used for
* entries associated with this DIT content rule.
*/
public Set<ObjectClass> getAuxiliaryClasses()
{
return auxiliaryClasses;
}
/**
* Indicates whether the provided auxiliary objectclass is allowed
* for use by this DIT content rule.
*
* @param auxiliaryClass The auxiliary objectclass for which to
* make the determination.
*
* @return {@code true} if the provided auxiliary objectclass is
* allowed for use by this DIT content rule, or
* {@code false} if not.
*/
public boolean isAllowedAuxiliaryClass(ObjectClass auxiliaryClass)
{
return auxiliaryClasses.contains(auxiliaryClass);
}
/**
* Retrieves the set of required attributes for this DIT content
* rule.
*
* @return The set of required attributes for this DIT content
* rule.
*/
public Set<AttributeType> getRequiredAttributes()
{
return requiredAttributes;
}
/**
* Indicates whether the provided attribute type is included in the
* required attribute list for this DIT content rule.
*
* @param attributeType The attribute type for which to make the
* determination.
*
* @return {@code true} if the provided attribute type is required
* by this DIT content rule, or {@code false} if not.
*/
public boolean isRequired(AttributeType attributeType)
{
return requiredAttributes.contains(attributeType);
}
/**
* Retrieves the set of optional attributes for this DIT content
* rule.
*
* @return The set of optional attributes for this DIT content
* rule.
*/
public Set<AttributeType> getOptionalAttributes()
{
return optionalAttributes;
}
/**
* Indicates whether the provided attribute type is included in the
* optional attribute list for this DIT content rule.
*
* @param attributeType The attribute type for which to make the
* determination.
*
* @return {@code true} if the provided attribute type is optional
* for this DIT content rule, or {@code false} if not.
*/
public boolean isOptional(AttributeType attributeType)
{
return optionalAttributes.contains(attributeType);
}
/**
* Indicates whether the provided attribute type is in the list of
* required or optional attributes for this DIT content rule.
*
* @param attributeType The attribute type for which to make the
* determination.
*
* @return {@code true} if the provided attribute type is required
* or allowed for this DIT content rule, or {@code false}
* if it is not.
*/
public boolean isRequiredOrOptional(AttributeType attributeType)
{
return (requiredAttributes.contains(attributeType) ||
optionalAttributes.contains(attributeType));
}
/**
* Indicates whether the provided attribute type is in the list of
* required or optional attributes for this DIT content rule.
*
* @param attributeType The attribute type for which to make the
* determination.
* @param acceptEmpty Indicates whether an empty list of
* required or optional attributes should be
* taken to indicate that all attributes
* allowed for an objectclass will be
* acceptable.
*
* @return {@code true} if the provided attribute type is required
* or allowed for this DIT content rule, or {@code false}
* if it is not.
*/
public boolean isRequiredOrOptional(AttributeType attributeType,
boolean acceptEmpty)
{
if (acceptEmpty &&
(requiredAttributes.isEmpty() ||
optionalAttributes.isEmpty()))
{
return true;
}
return (requiredAttributes.contains(attributeType) ||
optionalAttributes.contains(attributeType));
}
/**
* Retrieves the set of prohibited attributes for this DIT content
* rule.
*
* @return The set of prohibited attributes for this DIT content
* rule.
*/
public Set<AttributeType> getProhibitedAttributes()
{
return prohibitedAttributes;
}
/**
* Indicates whether the provided attribute type is included in the
* prohibited attribute list for this DIT content rule.
*
* @param attributeType The attribute type for which to make the
* determination.
*
* @return {@code true} if the provided attribute type is
* prohibited for this DIT content rule, or {@code false}
* if not.
*/
public boolean isProhibited(AttributeType attributeType)
{
return prohibitedAttributes.contains(attributeType);
}
/**
* Indicates whether this DIT content rule is declared "obsolete".
*
* @return {@code true} if this DIT content rule is declared
* "obsolete", or {@code false} if it is not.
*/
public boolean isObsolete()
{
return isObsolete;
}
/**
* Retrieves a mapping between the names of any extra non-standard
* properties that may be associated with this DIT content rule and
* the value for that property.
*
* @return A mapping between the names of any extra non-standard
* properties that may be associated with this DIT content
* rule and the value for that property.
*/
public Map<String,List<String>> getExtraProperties()
{
return extraProperties;
}
/**
* Retrieves the value of the specified "extra" property for this
* DIT content rule.
*
* @param propertyName The name of the "extra" property for which
* to retrieve the value.
*
* @return The value of the specified "extra" property for this DIT
* content rule, 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 DIT content
* rule.
*
* @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 DIT content
* rule.
*
* @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 DIT
* content rule. The object will be considered equal if it is a DIT
* content rule for the same structural objectclass and the same
* sets of names. For performance reasons, the set of auxiliary
* classes, and the sets of required, optional, and prohibited
* attribute types will not be checked, so that should be done
* manually if a more thorough equality comparison is required.
*
* @param o The object for which to make the determination.
*
* @return {@code true} if the provided object is equal to
* this DIT content rule, or {@code false} if not.
*/
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if ((o == null) || (! (o instanceof DITContentRule)))
{
return false;
}
DITContentRule dcr = (DITContentRule) o;
if (! structuralClass.equals(dcr.structuralClass))
{
return false;
}
if (names.size() != dcr.names.size())
{
return false;
}
Iterator<String> iterator = names.keySet().iterator();
while (iterator.hasNext())
{
if (! dcr.names.containsKey(iterator.next()))
{
return false;
}
}
return true;
}
/**
* Retrieves the hash code for this DIT content rule. It will be
* equal to the hash code for the associated structural objectclass.
*
* @return The hash code for this DIT content rule.
*/
public int hashCode()
{
return structuralClass.hashCode();
}
/**
* Retrieves the string representation of this DIT content rule in
* the form specified in RFC 2252.
*
* @return The string representation of this DIT content rule in
* the form specified in RFC 2252.
*/
public String toString()
{
StringBuilder buffer = new StringBuilder();
toString(buffer, true);
return buffer.toString();
}
/**
* Appends a string representation of this attribute type 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 DIT content rule was loaded.
*/
public void toString(StringBuilder buffer,
boolean includeFileElement)
{
buffer.append("( ");
buffer.append(structuralClass.getOID());
if (! names.isEmpty())
{
Iterator<String> iterator = names.values().iterator();
String firstName = iterator.next();
if (iterator.hasNext())
{
buffer.append(" NAME ( '");
buffer.append(firstName);
while (iterator.hasNext())
{
buffer.append("' '");
buffer.append(iterator.next());
}
buffer.append("' )");
}
else
{
buffer.append(" NAME '");
buffer.append(firstName);
buffer.append("'");
}
}
if ((description != null) && (description.length() > 0))
{
buffer.append(" DESC '");
buffer.append(description);
buffer.append("'");
}
if (isObsolete)
{
buffer.append(" OBSOLETE");
}
if (! auxiliaryClasses.isEmpty())
{
Iterator<ObjectClass> iterator = auxiliaryClasses.iterator();
String firstClass = iterator.next().getNameOrOID();
if (iterator.hasNext())
{
buffer.append(" AUX (");
buffer.append(firstClass);
while (iterator.hasNext())
{
buffer.append(" $ ");
buffer.append(iterator.next());
}
buffer.append(" )");
}
else
{
buffer.append(" AUX ");
buffer.append(firstClass);
}
}
if (! requiredAttributes.isEmpty())
{
Iterator<AttributeType> iterator =
requiredAttributes.iterator();
String firstName = iterator.next().getNameOrOID();
if (iterator.hasNext())
{
buffer.append(" MUST ( ");
buffer.append(firstName);
while (iterator.hasNext())
{
buffer.append(" $ ");
buffer.append(iterator.next().getNameOrOID());
}
buffer.append(" )");
}
else
{
buffer.append(" MUST ");
buffer.append(firstName);
}
}
if (! optionalAttributes.isEmpty())
{
Iterator<AttributeType> iterator =
optionalAttributes.iterator();
String firstName = iterator.next().getNameOrOID();
if (iterator.hasNext())
{
buffer.append(" MAY ( ");
buffer.append(firstName);
while (iterator.hasNext())
{
buffer.append(" $ ");
buffer.append(iterator.next().getNameOrOID());
}
buffer.append(" )");
}
else
{
buffer.append(" MAY ");
buffer.append(firstName);
}
}
if (! prohibitedAttributes.isEmpty())
{
Iterator<AttributeType> iterator =
prohibitedAttributes.iterator();
String firstName = iterator.next().getNameOrOID();
if (iterator.hasNext())
{
buffer.append(" NOT ( ");
buffer.append(firstName);
while (iterator.hasNext())
{
buffer.append(" $ ");
buffer.append(iterator.next().getNameOrOID());
}
buffer.append(" )");
}
else
{
buffer.append(" NOT ");
buffer.append(firstName);
}
}
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(" )");
}
}