/* * 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(); } }