/*
* 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.sdk.schema;
import static com.sun.opends.sdk.messages.Messages.*;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.opends.sdk.ByteSequence;
import org.opends.sdk.LocalizableMessage;
import org.opends.sdk.LocalizableMessageBuilder;
import com.sun.opends.sdk.util.Validator;
/**
* This class defines a data structure for storing and interacting with
* an LDAP syntaxes, which constrain the structure of attribute values
* stored in an LDAP directory, and determine the representation of
* attribute and assertion values transferred in the LDAP protocol.
*
* Syntax implementations must extend the
* SyntaxImplementation class so they can be used by OpenDS
* to validate attribute values.
*
* Where ordered sets of names, or extra properties are provided, the
* ordering will be preserved when the associated fields are accessed
* via their getters or via the {@link #toString()} methods.
*/
public final class Syntax extends SchemaElement
{
private final String oid;
private final String definition;
private MatchingRule equalityMatchingRule;
private MatchingRule orderingMatchingRule;
private MatchingRule substringMatchingRule;
private MatchingRule approximateMatchingRule;
private Schema schema;
private SyntaxImpl impl;
Syntax(String oid, String description,
Map> extraProperties, String definition,
SyntaxImpl implementation)
{
super(description, extraProperties);
Validator.ensureNotNull(oid);
this.oid = oid;
if (definition != null)
{
this.definition = definition;
}
else
{
this.definition = buildDefinition();
}
this.impl = implementation;
}
Syntax(String oid)
{
super("", Collections.singletonMap("X-SUBST",
Collections.singletonList(Schema.getDefaultSyntax().getOID())));
Validator.ensureNotNull(oid);
this.oid = oid;
this.definition = buildDefinition();
this.impl = Schema.getDefaultSyntax().impl;
}
/**
* 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} if approximate
* matches will not be allowed for this type by default.
*/
public MatchingRule getApproximateMatchingRule()
{
return approximateMatchingRule;
}
/**
* 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} if equality
* matches will not be allowed for this type by default.
*/
public MatchingRule getEqualityMatchingRule()
{
return equalityMatchingRule;
}
/**
* Retrieves the OID for this attribute syntax.
*
* @return The OID for this attribute syntax.
*/
public String getOID()
{
return oid;
}
/**
* 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} if ordering
* matches will not be allowed for this type by default.
*/
public MatchingRule getOrderingMatchingRule()
{
return orderingMatchingRule;
}
/**
* Retrieves the default substring matching rule that will be used for
* attributes with this syntax.
*
* @return The default substring matching rule that will be used for
* attributes with this syntax, or {@code null} if substring
* matches will not be allowed for this type by default.
*/
public MatchingRule getSubstringMatchingRule()
{
return substringMatchingRule;
}
/**
* Retrieves the hash code for this schema element. It will be
* calculated as the sum of the characters in the OID.
*
* @return The hash code for this attribute syntax.
*/
@Override
public int hashCode()
{
return getOID().hashCode();
}
/**
* Indicates whether this attribute syntax requires that values must
* be encoded using the Basic Encoding Rules (BER) used by X.500
* directories and always include the {@code binary} attribute
* description option.
*
* @return {@code true} this attribute syntax requires that values
* must be BER encoded and always include the {@code binary}
* attribute description option, or {@code false} if not.
* @see RFC 4522 -
* Lightweight Directory Access Protocol (LDAP): The Binary
* Encoding Option
*/
public boolean isBEREncodingRequired()
{
return impl.isBEREncodingRequired();
}
/**
* Indicates whether this attribute syntax would likely be a human
* readable string.
*
* @return {@code true} if this attribute syntax would likely be a
* human readable string or {@code false} if not.
*/
public boolean isHumanReadable()
{
return impl.isHumanReadable();
}
/**
* Retrieves a string representation of this attribute syntax in the
* format defined in RFC 2252.
*
* @return A string representation of this attribute syntax in the
* format defined in RFC 2252.
*/
@Override
public String toString()
{
return definition;
}
/**
* 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} if the provided value is acceptable for use
* with this syntax, or {@code false} if not.
*/
public boolean valueIsAcceptable(ByteSequence value,
LocalizableMessageBuilder invalidReason)
{
return impl.valueIsAcceptable(schema, value, invalidReason);
}
Syntax duplicate()
{
return new Syntax(oid, description, extraProperties, definition,
impl);
}
@Override
void toStringContent(StringBuilder buffer)
{
buffer.append(oid);
if (description != null && description.length() > 0)
{
buffer.append(" DESC '");
buffer.append(description);
buffer.append("'");
}
}
@Override
void validate(List warnings, Schema schema)
throws SchemaException
{
this.schema = schema;
if (impl == null)
{
// See if we need to override the implementation of the syntax
for (final Map.Entry> property : extraProperties
.entrySet())
{
// Enums are handled in the schema builder.
if (property.getKey().equalsIgnoreCase("x-subst"))
{
/**
* One unimplemented syntax can be substituted by another
* defined syntax. A substitution syntax is an
* LDAPSyntaxDescriptionSyntax with X-SUBST extension.
*/
final Iterator values =
property.getValue().iterator();
if (values.hasNext())
{
final String value = values.next();
if (value.equals(oid))
{
final LocalizableMessage message =
ERR_ATTR_SYNTAX_CYCLIC_SUB_SYNTAX.get(oid);
throw new SchemaException(message);
}
if (!schema.hasSyntax(value))
{
final LocalizableMessage message =
ERR_ATTR_SYNTAX_UNKNOWN_SUB_SYNTAX.get(oid, value);
throw new SchemaException(message);
}
final Syntax subSyntax = schema.getSyntax(value);
if (subSyntax.impl == null)
{
// The substitution syntax was never validated.
subSyntax.validate(warnings, schema);
}
impl = subSyntax.impl;
}
}
else if (property.getKey().equalsIgnoreCase("x-pattern"))
{
final Iterator values =
property.getValue().iterator();
if (values.hasNext())
{
final String value = values.next();
try
{
final Pattern pattern = Pattern.compile(value);
impl = new RegexSyntaxImpl(pattern);
}
catch (final Exception e)
{
final LocalizableMessage message =
WARN_ATTR_SYNTAX_LDAPSYNTAX_REGEX_INVALID_PATTERN
.get(oid, value);
throw new SchemaException(message);
}
}
}
}
// Try to find an implementation in the core schema
if (impl == null && Schema.getDefaultSchema().hasSyntax(oid))
{
impl = Schema.getDefaultSchema().getSyntax(oid).impl;
}
if (impl == null && Schema.getCoreSchema().hasSyntax(oid))
{
impl = Schema.getCoreSchema().getSyntax(oid).impl;
}
if (impl == null)
{
impl = Schema.getDefaultSyntax().impl;
final LocalizableMessage message =
WARN_ATTR_SYNTAX_NOT_IMPLEMENTED.get(oid, Schema
.getDefaultSyntax().getOID());
warnings.add(message);
}
}
// Get references to the default matching rules. It will be ok
// if we can't find some. Just warn.
if (impl.getEqualityMatchingRule() != null)
{
if (schema.hasMatchingRule(impl.getEqualityMatchingRule()))
{
equalityMatchingRule =
schema.getMatchingRule(impl.getEqualityMatchingRule());
}
else
{
final LocalizableMessage message =
ERR_ATTR_SYNTAX_UNKNOWN_EQUALITY_MATCHING_RULE.get(impl
.getEqualityMatchingRule(), impl.getName());
warnings.add(message);
}
}
if (impl.getOrderingMatchingRule() != null)
{
if (schema.hasMatchingRule(impl.getOrderingMatchingRule()))
{
orderingMatchingRule =
schema.getMatchingRule(impl.getOrderingMatchingRule());
}
else
{
final LocalizableMessage message =
ERR_ATTR_SYNTAX_UNKNOWN_ORDERING_MATCHING_RULE.get(impl
.getOrderingMatchingRule(), impl.getName());
warnings.add(message);
}
}
if (impl.getSubstringMatchingRule() != null)
{
if (schema.hasMatchingRule(impl.getSubstringMatchingRule()))
{
substringMatchingRule =
schema.getMatchingRule(impl.getSubstringMatchingRule());
}
else
{
final LocalizableMessage message =
ERR_ATTR_SYNTAX_UNKNOWN_SUBSTRING_MATCHING_RULE.get(impl
.getSubstringMatchingRule(), impl.getName());
warnings.add(message);
}
}
if (impl.getApproximateMatchingRule() != null)
{
if (schema.hasMatchingRule(impl.getApproximateMatchingRule()))
{
approximateMatchingRule =
schema.getMatchingRule(impl.getApproximateMatchingRule());
}
else
{
final LocalizableMessage message =
ERR_ATTR_SYNTAX_UNKNOWN_APPROXIMATE_MATCHING_RULE.get(impl
.getApproximateMatchingRule(), impl.getName());
warnings.add(message);
}
}
}
}