/*
* 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 legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* 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 legal-notices/CDDLv1_0.txt.
* 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 2015 ForgeRock AS
*/
package org.opends.server.types;
import static org.opends.server.util.StaticUtils.*;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.forgerock.util.Reject;
/** Temporary class until we move to {@link org.forgerock.opendj.ldap.AttributeDescription}. */
public final class AttributeDescription implements Comparable<AttributeDescription>
{
private final AttributeType attributeType;
private final Set<String> options;
private AttributeDescription(AttributeType attributeType, Set<String> options)
{
Reject.ifNull(attributeType);
Reject.ifNull(options);
this.attributeType = attributeType;
this.options = options;
}
/**
* Creates an attribute description with the attribute type and options of the provided
* {@link Attribute}.
*
* @param attr
* The attribute.
* @return The attribute description.
* @throws NullPointerException
* If {@code attributeType} or {@code options} was {@code null}.
*/
public static AttributeDescription create(Attribute attr)
{
return create(attr.getAttributeType(), attr.getOptions());
}
/**
* Creates an attribute description having the provided attribute type and options.
*
* @param attributeType
* The attribute type.
* @param options
* The attribute options.
* @return The attribute description.
* @throws NullPointerException
* If {@code attributeType} or {@code options} was {@code null}.
*/
public static AttributeDescription create(AttributeType attributeType, Set<String> options)
{
return new AttributeDescription(attributeType, options);
}
/**
* Returns the attribute type associated with this attribute description.
*
* @return The attribute type associated with this attribute description.
*/
public AttributeType getAttributeType()
{
return attributeType;
}
/**
* Returns the set of not normalized options contained in this attribute description.
*
* @return A set containing the not normalized options.
*/
public Set<String> getOptions()
{
return options;
}
/**
* Indicates whether the provided set of not normalized options contains the provided option.
*
* @param options
* The set of not normalized options where to do the search.
* @param optionToFind
* The option for which to make the determination.
* @return {@code true} if the provided set of options has the provided option, or {@code false}
* if not.
*/
public static boolean containsOption(Set<String> options, String optionToFind)
{
String normToFind = toLowerCase(optionToFind);
// Cannot use Set.contains() because the options are not normalized.
for (String o : options)
{
String norm = toLowerCase(o);
if (norm.equals(normToFind))
{
return true;
}
}
return false;
}
/**
* Indicates whether the provided first set of not normalized options contains all values from the
* second set of not normalized options.
*
* @param options1
* The first set of not normalized options where to do the search.
* @param options2
* The second set of not normalized options that must all be found.
* @return {@code true} if the first provided set of options has all the options from the second
* provided set of options.
*/
public static boolean containsAllOptions(Collection<String> options1, Collection<String> options2)
{
if (options1 == options2)
{
return true;
}
else if (isEmpty(options2))
{
return true;
}
else if (isEmpty(options1))
{
return false;
}
// normalize all options before calling containsAll()
Set<String> set1 = toLowercaseSet(options1);
Set<String> set2 = toLowercaseSet(options2);
return set1.size() >= set2.size() && set1.containsAll(set2);
}
/**
* Indicates whether the provided first set of not normalized options equals the second set of not
* normalized options.
*
* @param options1
* The first set of not normalized options.
* @param options2
* The second set of not normalized options.
* @return {@code true} if the first provided set of options equals the second provided set of
* options.
*/
public static boolean optionsEqual(Set<String> options1, Set<String> options2)
{
if (options1 == options2)
{
return true;
}
else if (isEmpty(options2))
{
return isEmpty(options1);
}
else if (isEmpty(options1))
{
return false;
}
// normalize all options before calling containsAll()
Set<String> set1 = toLowercaseSet(options1);
Set<String> set2 = toLowercaseSet(options2);
return set1.equals(set2);
}
private static boolean isEmpty(Collection<String> col)
{
return col == null || col.isEmpty();
}
private static SortedSet<String> toLowercaseSet(Collection<String> strings)
{
final SortedSet<String> results = new TreeSet<>();
for (String s : strings)
{
results.add(toLowerCase(s));
}
return results;
}
@Override
public int compareTo(AttributeDescription other)
{
if (this == other)
{
return 0;
}
return compare(attributeType, options, other.attributeType, other.options);
}
/**
* Compares the first attribute type and options to the second attribute type and options, as if
* they were both instances of {@link AttributeDescription}.
* <p>
* The attribute types are compared first and then, if equal, the options are normalized, sorted,
* and compared.
*
* @param attrType1
* The first attribute type to be compared.
* @param options1
* The first options to be compared.
* @param attrType2
* The second attribute type to be compared.
* @param options2
* The second options to be compared.
* @return A negative integer, zero, or a positive integer as this attribute description is less
* than, equal to, or greater than the specified attribute description.
* @throws NullPointerException
* If {@code name} was {@code null}.
* @see AttributeDescription#compareTo(AttributeDescription)
*/
public static int compare(AttributeType attrType1, Set<String> options1,
AttributeType attrType2, Set<String> options2)
{
int cmp = attrType1.compareTo(attrType2);
if (cmp != 0)
{
return cmp;
}
if (options1 == options2)
{
return 0;
}
return compare(toLowercaseSet(options1), toLowercaseSet(options2));
}
private static int compare(SortedSet<String> options1, SortedSet<String> options2)
{
Iterator<String> it1 = options1.iterator();
Iterator<String> it2 = options2.iterator();
while (it1.hasNext() && it2.hasNext())
{
int cmp = it1.next().compareTo(it2.next());
if (cmp != 0)
{
return cmp;
}
}
if (it1.hasNext())
{
return 1;
}
else if (it2.hasNext())
{
return -1;
}
return 0;
}
@Override
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
if (!(obj instanceof AttributeDescription))
{
return false;
}
final AttributeDescription other = (AttributeDescription) obj;
return attributeType.equals(other.attributeType) && optionsEqual(options, other.options);
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((attributeType == null) ? 0 : attributeType.hashCode());
result = prime * result + ((options == null) ? 0 : options.hashCode());
return result;
}
@Override
public String toString()
{
final StringBuilder buffer = new StringBuilder();
buffer.append(attributeType.getNameOrOID());
for (String option : options)
{
buffer.append(';');
buffer.append(option);
}
return buffer.toString();
}
}