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 * 024 * Copyright 2009 Sun Microsystems, Inc. 025 * Portions copyright 2012-2015 ForgeRock AS. 026 */ 027 028package org.forgerock.opendj.ldap; 029 030import java.util.AbstractSet; 031import java.util.Collection; 032import java.util.HashMap; 033import java.util.Iterator; 034import java.util.Map; 035 036import org.forgerock.opendj.ldap.schema.AttributeType; 037import org.forgerock.opendj.ldap.schema.MatchingRule; 038 039import org.forgerock.util.Reject; 040 041/** 042 * This class provides a skeletal implementation of the {@code Attribute} 043 * interface, to minimize the effort required to implement this interface. 044 */ 045public abstract class AbstractAttribute extends AbstractSet<ByteString> implements Attribute { 046 047 /** 048 * Returns {@code true} if {@code object} is an attribute which is equal to 049 * {@code attribute}. Two attributes are considered equal if their attribute 050 * descriptions are equal, they both have the same number of attribute 051 * values, and every attribute value contained in the first attribute is 052 * also contained in the second attribute. 053 * 054 * @param attribute 055 * The attribute to be tested for equality. 056 * @param object 057 * The object to be tested for equality with the attribute. 058 * @return {@code true} if {@code object} is an attribute which is equal to 059 * {@code attribute}, or {@code false} if not. 060 */ 061 static boolean equals(final Attribute attribute, final Object object) { 062 if (attribute == object) { 063 return true; 064 } 065 if (!(object instanceof Attribute)) { 066 return false; 067 } 068 069 final Attribute other = (Attribute) object; 070 return attribute.getAttributeDescription().equals(other.getAttributeDescription()) 071 && attribute.size() == other.size() 072 && attribute.containsAll(other); 073 } 074 075 /** 076 * Returns the hash code for {@code attribute}. It will be calculated as the 077 * sum of the hash codes of the attribute description and all of the 078 * attribute values. 079 * 080 * @param attribute 081 * The attribute whose hash code should be calculated. 082 * @return The hash code for {@code attribute}. 083 */ 084 static int hashCode(final Attribute attribute) { 085 int hashCode = attribute.getAttributeDescription().hashCode(); 086 for (final ByteString value : attribute) { 087 hashCode += normalizeValue(attribute, value).hashCode(); 088 } 089 return hashCode; 090 } 091 092 /** 093 * Returns the normalized form of {@code value} normalized using 094 * {@code attribute}'s equality matching rule. 095 * 096 * @param attribute 097 * The attribute whose equality matching rule should be used for 098 * normalization. 099 * @param value 100 * The attribute value to be normalized. 101 * @return The normalized form of {@code value} normalized using 102 * {@code attribute}'s equality matching rule. 103 */ 104 static ByteString normalizeValue(final Attribute attribute, final ByteString value) { 105 final AttributeDescription attributeDescription = attribute.getAttributeDescription(); 106 final AttributeType attributeType = attributeDescription.getAttributeType(); 107 final MatchingRule matchingRule = attributeType.getEqualityMatchingRule(); 108 109 try { 110 return matchingRule.normalizeAttributeValue(value); 111 } catch (final Exception e) { 112 // Fall back to provided value. 113 return value; 114 } 115 } 116 117 /** 118 * Returns a string representation of {@code attribute}. 119 * 120 * @param attribute 121 * The attribute whose string representation should be returned. 122 * @return The string representation of {@code attribute}. 123 */ 124 static String toString(final Attribute attribute) { 125 final StringBuilder builder = new StringBuilder(); 126 builder.append('"'); 127 builder.append(attribute.getAttributeDescriptionAsString()); 128 builder.append("\":["); 129 boolean firstValue = true; 130 for (final ByteString value : attribute) { 131 if (!firstValue) { 132 builder.append(','); 133 } 134 builder.append('"'); 135 builder.append(value); 136 builder.append('"'); 137 firstValue = false; 138 } 139 builder.append(']'); 140 return builder.toString(); 141 } 142 143 /** 144 * Sole constructor. 145 */ 146 protected AbstractAttribute() { 147 // No implementation required. 148 } 149 150 /** {@inheritDoc} */ 151 @Override 152 public abstract boolean add(ByteString value); 153 154 /** {@inheritDoc} */ 155 public boolean add(final Object... values) { 156 Reject.ifNull(values); 157 boolean modified = false; 158 for (final Object value : values) { 159 modified |= add(ByteString.valueOf(value)); 160 } 161 return modified; 162 } 163 164 /** {@inheritDoc} */ 165 @Override 166 public boolean addAll(final Collection<? extends ByteString> values) { 167 return addAll(values, null); 168 } 169 170 /** {@inheritDoc} */ 171 public <T> boolean addAll(final Collection<T> values, 172 final Collection<? super T> duplicateValues) { 173 boolean modified = false; 174 for (final T value : values) { 175 if (add(value)) { 176 modified = true; 177 } else if (duplicateValues != null) { 178 duplicateValues.add(value); 179 } 180 } 181 return modified; 182 } 183 184 /** {@inheritDoc} */ 185 @Override 186 public abstract boolean contains(Object value); 187 188 /** {@inheritDoc} */ 189 @Override 190 public boolean containsAll(final Collection<?> values) { 191 for (final Object value : values) { 192 if (!contains(value)) { 193 return false; 194 } 195 } 196 return true; 197 } 198 199 /** {@inheritDoc} */ 200 @Override 201 public boolean equals(final Object object) { 202 return equals(this, object); 203 } 204 205 /** {@inheritDoc} */ 206 public ByteString firstValue() { 207 return iterator().next(); 208 } 209 210 /** {@inheritDoc} */ 211 public String firstValueAsString() { 212 return firstValue().toString(); 213 } 214 215 /** {@inheritDoc} */ 216 public abstract AttributeDescription getAttributeDescription(); 217 218 /** {@inheritDoc} */ 219 public String getAttributeDescriptionAsString() { 220 return getAttributeDescription().toString(); 221 } 222 223 /** {@inheritDoc} */ 224 @Override 225 public int hashCode() { 226 return hashCode(this); 227 } 228 229 /** {@inheritDoc} */ 230 public AttributeParser parse() { 231 return AttributeParser.parseAttribute(this); 232 } 233 234 /** {@inheritDoc} */ 235 @Override 236 public abstract Iterator<ByteString> iterator(); 237 238 /** {@inheritDoc} */ 239 @Override 240 public abstract boolean remove(Object value); 241 242 /** {@inheritDoc} */ 243 @Override 244 public boolean removeAll(final Collection<?> values) { 245 return removeAll(values, null); 246 } 247 248 /** {@inheritDoc} */ 249 public <T> boolean removeAll(final Collection<T> values, 250 final Collection<? super T> missingValues) { 251 boolean modified = false; 252 for (final T value : values) { 253 if (remove(value)) { 254 modified = true; 255 } else if (missingValues != null) { 256 missingValues.add(value); 257 } 258 } 259 return modified; 260 } 261 262 /** {@inheritDoc} */ 263 @Override 264 public boolean retainAll(final Collection<?> values) { 265 return retainAll(values, null); 266 } 267 268 /** {@inheritDoc} */ 269 public <T> boolean retainAll(final Collection<T> values, 270 final Collection<? super T> missingValues) { 271 if (values.isEmpty()) { 272 if (isEmpty()) { 273 return false; 274 } else { 275 clear(); 276 return true; 277 } 278 } 279 280 if (isEmpty()) { 281 if (missingValues != null) { 282 missingValues.addAll(values); 283 } 284 return false; 285 } 286 287 final Map<ByteString, T> valuesToRetain = new HashMap<>(values.size()); 288 for (final T value : values) { 289 valuesToRetain.put(normalizeValue(this, ByteString.valueOf(value)), value); 290 } 291 292 boolean modified = false; 293 final Iterator<ByteString> iterator = iterator(); 294 while (iterator.hasNext()) { 295 final ByteString value = iterator.next(); 296 final ByteString normalizedValue = normalizeValue(this, value); 297 if (valuesToRetain.remove(normalizedValue) == null) { 298 modified = true; 299 iterator.remove(); 300 } 301 } 302 303 if (missingValues != null) { 304 missingValues.addAll(valuesToRetain.values()); 305 } 306 307 return modified; 308 } 309 310 /** {@inheritDoc} */ 311 @Override 312 public abstract int size(); 313 314 /** {@inheritDoc} */ 315 @Override 316 public ByteString[] toArray() { 317 return toArray(new ByteString[size()]); 318 } 319 320 /** {@inheritDoc} */ 321 @Override 322 public String toString() { 323 return toString(this); 324 } 325 326}