001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * Copyright 2015 ForgeRock AS 024 */ 025package org.opends.server.types; 026 027import static org.opends.server.util.StaticUtils.*; 028 029import java.util.Collection; 030import java.util.Iterator; 031import java.util.Set; 032import java.util.SortedSet; 033import java.util.TreeSet; 034 035import org.forgerock.util.Reject; 036 037/** Temporary class until we move to {@link org.forgerock.opendj.ldap.AttributeDescription}. */ 038public final class AttributeDescription implements Comparable<AttributeDescription> 039{ 040 private final AttributeType attributeType; 041 private final Set<String> options; 042 043 private AttributeDescription(AttributeType attributeType, Set<String> options) 044 { 045 Reject.ifNull(attributeType); 046 Reject.ifNull(options); 047 this.attributeType = attributeType; 048 this.options = options; 049 } 050 051 /** 052 * Creates an attribute description with the attribute type and options of the provided 053 * {@link Attribute}. 054 * 055 * @param attr 056 * The attribute. 057 * @return The attribute description. 058 * @throws NullPointerException 059 * If {@code attributeType} or {@code options} was {@code null}. 060 */ 061 public static AttributeDescription create(Attribute attr) 062 { 063 return create(attr.getAttributeType(), attr.getOptions()); 064 } 065 066 /** 067 * Creates an attribute description having the provided attribute type and options. 068 * 069 * @param attributeType 070 * The attribute type. 071 * @param options 072 * The attribute options. 073 * @return The attribute description. 074 * @throws NullPointerException 075 * If {@code attributeType} or {@code options} was {@code null}. 076 */ 077 public static AttributeDescription create(AttributeType attributeType, Set<String> options) 078 { 079 return new AttributeDescription(attributeType, options); 080 } 081 082 /** 083 * Returns the attribute type associated with this attribute description. 084 * 085 * @return The attribute type associated with this attribute description. 086 */ 087 public AttributeType getAttributeType() 088 { 089 return attributeType; 090 } 091 092 /** 093 * Returns the set of not normalized options contained in this attribute description. 094 * 095 * @return A set containing the not normalized options. 096 */ 097 public Set<String> getOptions() 098 { 099 return options; 100 } 101 102 /** 103 * Indicates whether the provided set of not normalized options contains the provided option. 104 * 105 * @param options 106 * The set of not normalized options where to do the search. 107 * @param optionToFind 108 * The option for which to make the determination. 109 * @return {@code true} if the provided set of options has the provided option, or {@code false} 110 * if not. 111 */ 112 public static boolean containsOption(Set<String> options, String optionToFind) 113 { 114 String normToFind = toLowerCase(optionToFind); 115 116 // Cannot use Set.contains() because the options are not normalized. 117 for (String o : options) 118 { 119 String norm = toLowerCase(o); 120 if (norm.equals(normToFind)) 121 { 122 return true; 123 } 124 } 125 return false; 126 } 127 128 /** 129 * Indicates whether the provided first set of not normalized options contains all values from the 130 * second set of not normalized options. 131 * 132 * @param options1 133 * The first set of not normalized options where to do the search. 134 * @param options2 135 * The second set of not normalized options that must all be found. 136 * @return {@code true} if the first provided set of options has all the options from the second 137 * provided set of options. 138 */ 139 public static boolean containsAllOptions(Collection<String> options1, Collection<String> options2) 140 { 141 if (options1 == options2) 142 { 143 return true; 144 } 145 else if (isEmpty(options2)) 146 { 147 return true; 148 } 149 else if (isEmpty(options1)) 150 { 151 return false; 152 } 153 // normalize all options before calling containsAll() 154 Set<String> set1 = toLowercaseSet(options1); 155 Set<String> set2 = toLowercaseSet(options2); 156 return set1.size() >= set2.size() && set1.containsAll(set2); 157 } 158 159 /** 160 * Indicates whether the provided first set of not normalized options equals the second set of not 161 * normalized options. 162 * 163 * @param options1 164 * The first set of not normalized options. 165 * @param options2 166 * The second set of not normalized options. 167 * @return {@code true} if the first provided set of options equals the second provided set of 168 * options. 169 */ 170 public static boolean optionsEqual(Set<String> options1, Set<String> options2) 171 { 172 if (options1 == options2) 173 { 174 return true; 175 } 176 else if (isEmpty(options2)) 177 { 178 return isEmpty(options1); 179 } 180 else if (isEmpty(options1)) 181 { 182 return false; 183 } 184 // normalize all options before calling containsAll() 185 Set<String> set1 = toLowercaseSet(options1); 186 Set<String> set2 = toLowercaseSet(options2); 187 return set1.equals(set2); 188 } 189 190 private static boolean isEmpty(Collection<String> col) 191 { 192 return col == null || col.isEmpty(); 193 } 194 195 private static SortedSet<String> toLowercaseSet(Collection<String> strings) 196 { 197 final SortedSet<String> results = new TreeSet<>(); 198 for (String s : strings) 199 { 200 results.add(toLowerCase(s)); 201 } 202 return results; 203 } 204 205 @Override 206 public int compareTo(AttributeDescription other) 207 { 208 if (this == other) 209 { 210 return 0; 211 } 212 return compare(attributeType, options, other.attributeType, other.options); 213 } 214 215 /** 216 * Compares the first attribute type and options to the second attribute type and options, as if 217 * they were both instances of {@link AttributeDescription}. 218 * <p> 219 * The attribute types are compared first and then, if equal, the options are normalized, sorted, 220 * and compared. 221 * 222 * @param attrType1 223 * The first attribute type to be compared. 224 * @param options1 225 * The first options to be compared. 226 * @param attrType2 227 * The second attribute type to be compared. 228 * @param options2 229 * The second options to be compared. 230 * @return A negative integer, zero, or a positive integer as this attribute description is less 231 * than, equal to, or greater than the specified attribute description. 232 * @throws NullPointerException 233 * If {@code name} was {@code null}. 234 * @see AttributeDescription#compareTo(AttributeDescription) 235 */ 236 public static int compare(AttributeType attrType1, Set<String> options1, 237 AttributeType attrType2, Set<String> options2) 238 { 239 int cmp = attrType1.compareTo(attrType2); 240 if (cmp != 0) 241 { 242 return cmp; 243 } 244 if (options1 == options2) 245 { 246 return 0; 247 } 248 return compare(toLowercaseSet(options1), toLowercaseSet(options2)); 249 } 250 251 private static int compare(SortedSet<String> options1, SortedSet<String> options2) 252 { 253 Iterator<String> it1 = options1.iterator(); 254 Iterator<String> it2 = options2.iterator(); 255 while (it1.hasNext() && it2.hasNext()) 256 { 257 int cmp = it1.next().compareTo(it2.next()); 258 if (cmp != 0) 259 { 260 return cmp; 261 } 262 } 263 if (it1.hasNext()) 264 { 265 return 1; 266 } 267 else if (it2.hasNext()) 268 { 269 return -1; 270 } 271 return 0; 272 } 273 274 @Override 275 public boolean equals(Object obj) 276 { 277 if (obj == this) 278 { 279 return true; 280 } 281 if (!(obj instanceof AttributeDescription)) 282 { 283 return false; 284 } 285 final AttributeDescription other = (AttributeDescription) obj; 286 return attributeType.equals(other.attributeType) && optionsEqual(options, other.options); 287 } 288 289 @Override 290 public int hashCode() 291 { 292 final int prime = 31; 293 int result = 1; 294 result = prime * result + ((attributeType == null) ? 0 : attributeType.hashCode()); 295 result = prime * result + ((options == null) ? 0 : options.hashCode()); 296 return result; 297 } 298 299 @Override 300 public String toString() 301 { 302 final StringBuilder buffer = new StringBuilder(); 303 buffer.append(attributeType.getNameOrOID()); 304 for (String option : options) 305 { 306 buffer.append(';'); 307 buffer.append(option); 308 } 309 return buffer.toString(); 310 } 311}