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