1 /* 2 * Copyright (c) 1999, 2004, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 27 package javax.naming.directory; 28 29 import java.util.Hashtable; 30 import java.util.Enumeration; 31 32 import javax.naming.NamingException; 33 import javax.naming.NamingEnumeration; 34 35 /** 36 * This class provides a basic implementation 37 * of the Attributes interface. 38 *<p> 39 * BasicAttributes is either case-sensitive or case-insensitive (case-ignore). 40 * This property is determined at the time the BasicAttributes constructor 41 * is called. 42 * In a case-insensitive BasicAttributes, the case of its attribute identifiers 43 * is ignored when searching for an attribute, or adding attributes. 44 * In a case-sensitive BasicAttributes, the case is significant. 45 *<p> 46 * When the BasicAttributes class needs to create an Attribute, it 47 * uses BasicAttribute. There is no other dependency on BasicAttribute. 48 *<p> 49 * Note that updates to BasicAttributes (such as adding or removing an attribute) 50 * does not affect the corresponding representation in the directory. 51 * Updates to the directory can only be effected 52 * using operations in the DirContext interface. 53 *<p> 54 * A BasicAttributes instance is not synchronized against concurrent 55 * multithreaded access. Multiple threads trying to access and modify 56 * a single BasicAttributes instance should lock the object. 57 * 58 * @author Rosanna Lee 59 * @author Scott Seligman 60 * 61 * @see DirContext#getAttributes 62 * @see DirContext#modifyAttributes 63 * @see DirContext#bind 64 * @see DirContext#rebind 65 * @see DirContext#createSubcontext 66 * @see DirContext#search 67 * @since 1.3 68 */ 69 70 public class BasicAttributes implements Attributes { 71 /** 72 * Indicates whether case of attribute ids is ignored. 73 * @serial 74 */ 75 private boolean ignoreCase = false; 76 77 // The 'key' in attrs is stored in the 'right case'. 78 // If ignoreCase is true, key is aways lowercase. 79 // If ignoreCase is false, key is stored as supplied by put(). 80 // %%% Not declared "private" due to bug 4064984. 81 transient Hashtable<String,Attribute> attrs = new Hashtable<>(11); 82 83 /** 84 * Constructs a new instance of Attributes. 85 * The character case of attribute identifiers 86 * is significant when subsequently retrieving or adding attributes. 87 */ 88 public BasicAttributes() { 89 } 90 91 /** 92 * Constructs a new instance of Attributes. 93 * If <code>ignoreCase</code> is true, the character case of attribute 94 * identifiers is ignored; otherwise the case is significant. 95 * @param ignoreCase true means this attribute set will ignore 96 * the case of its attribute identifiers 97 * when retrieving or adding attributes; 98 * false means case is respected. 99 */ 100 public BasicAttributes(boolean ignoreCase) { 101 this.ignoreCase = ignoreCase; 102 } 103 104 /** 105 * Constructs a new instance of Attributes with one attribute. 106 * The attribute specified by attrID and val are added to the newly 107 * created attribute. 108 * The character case of attribute identifiers 109 * is significant when subsequently retrieving or adding attributes. 110 * @param attrID non-null The id of the attribute to add. 111 * @param val The value of the attribute to add. If null, a null 112 * value is added to the attribute. 113 */ 114 public BasicAttributes(String attrID, Object val) { 115 this(); 116 this.put(new BasicAttribute(attrID, val)); 117 } 118 119 /** 120 * Constructs a new instance of Attributes with one attribute. 121 * The attribute specified by attrID and val are added to the newly 122 * created attribute. 123 * If <code>ignoreCase</code> is true, the character case of attribute 124 * identifiers is ignored; otherwise the case is significant. 125 * @param attrID non-null The id of the attribute to add. 126 * If this attribute set ignores the character 127 * case of its attribute ids, the case of attrID 128 * is ignored. 129 * @param val The value of the attribute to add. If null, a null 130 * value is added to the attribute. 131 * @param ignoreCase true means this attribute set will ignore 132 * the case of its attribute identifiers 133 * when retrieving or adding attributes; 134 * false means case is respected. 135 */ 136 public BasicAttributes(String attrID, Object val, boolean ignoreCase) { 137 this(ignoreCase); 138 this.put(new BasicAttribute(attrID, val)); 139 } 140 141 @SuppressWarnings("unchecked") 142 public Object clone() { 143 BasicAttributes attrset; 144 try { 145 attrset = (BasicAttributes)super.clone(); 146 } catch (CloneNotSupportedException e) { 147 attrset = new BasicAttributes(ignoreCase); 148 } 149 attrset.attrs = (Hashtable<String,Attribute>)attrs.clone(); 150 return attrset; 151 } 152 153 public boolean isCaseIgnored() { 154 return ignoreCase; 155 } 156 157 public int size() { 158 return attrs.size(); 159 } 160 161 public Attribute get(String attrID) { 162 Attribute attr = attrs.get( 163 ignoreCase ? attrID.toLowerCase() : attrID); 164 return (attr); 165 } 166 167 public NamingEnumeration<Attribute> getAll() { 168 return new AttrEnumImpl(); 169 } 170 171 public NamingEnumeration<String> getIDs() { 172 return new IDEnumImpl(); 173 } 174 175 public Attribute put(String attrID, Object val) { 176 return this.put(new BasicAttribute(attrID, val)); 177 } 178 179 public Attribute put(Attribute attr) { 180 String id = attr.getID(); 181 if (ignoreCase) { 182 id = id.toLowerCase(); 183 } 184 return attrs.put(id, attr); 185 } 186 187 public Attribute remove(String attrID) { 188 String id = (ignoreCase ? attrID.toLowerCase() : attrID); 189 return attrs.remove(id); 190 } 191 192 /** 193 * Generates the string representation of this attribute set. 194 * The string consists of each attribute identifier and the contents 195 * of each attribute. The contents of this string is useful 196 * for debugging and is not meant to be interpreted programmatically. 197 * 198 * @return A non-null string listing the contents of this attribute set. 199 */ 200 public String toString() { 201 if (attrs.size() == 0) { 202 return("No attributes"); 203 } else { 204 return attrs.toString(); 205 } 206 } 207 208 /** 209 * Determines whether this <tt>BasicAttributes</tt> is equal to another 210 * <tt>Attributes</tt> 211 * Two <tt>Attributes</tt> are equal if they are both instances of 212 * <tt>Attributes</tt>, 213 * treat the case of attribute IDs the same way, and contain the 214 * same attributes. Each <tt>Attribute</tt> in this <tt>BasicAttributes</tt> 215 * is checked for equality using <tt>Object.equals()</tt>, which may have 216 * be overridden by implementations of <tt>Attribute</tt>). 217 * If a subclass overrides <tt>equals()</tt>, 218 * it should override <tt>hashCode()</tt> 219 * as well so that two <tt>Attributes</tt> instances that are equal 220 * have the same hash code. 221 * @param obj the possibly null object to compare against. 222 * 223 * @return true If obj is equal to this BasicAttributes. 224 * @see #hashCode 225 */ 226 public boolean equals(Object obj) { 227 if ((obj != null) && (obj instanceof Attributes)) { 228 Attributes target = (Attributes)obj; 229 230 // Check case first 231 if (ignoreCase != target.isCaseIgnored()) { 232 return false; 233 } 234 235 if (size() == target.size()) { 236 Attribute their, mine; 237 try { 238 NamingEnumeration<?> theirs = target.getAll(); 239 while (theirs.hasMore()) { 240 their = (Attribute)theirs.next(); 241 mine = get(their.getID()); 242 if (!their.equals(mine)) { 243 return false; 244 } 245 } 246 } catch (NamingException e) { 247 return false; 248 } 249 return true; 250 } 251 } 252 return false; 253 } 254 255 /** 256 * Calculates the hash code of this BasicAttributes. 257 *<p> 258 * The hash code is computed by adding the hash code of 259 * the attributes of this object. If this BasicAttributes 260 * ignores case of its attribute IDs, one is added to the hash code. 261 * If a subclass overrides <tt>hashCode()</tt>, 262 * it should override <tt>equals()</tt> 263 * as well so that two <tt>Attributes</tt> instances that are equal 264 * have the same hash code. 265 * 266 * @return an int representing the hash code of this BasicAttributes instance. 267 * @see #equals 268 */ 269 public int hashCode() { 270 int hash = (ignoreCase ? 1 : 0); 271 try { 272 NamingEnumeration<?> all = getAll(); 273 while (all.hasMore()) { 274 hash += all.next().hashCode(); 275 } 276 } catch (NamingException e) {} 277 return hash; 278 } 279 280 /** 281 * Overridden to avoid exposing implementation details. 282 * @serialData Default field (ignoreCase flag -- a boolean), followed by 283 * the number of attributes in the set 284 * (an int), and then the individual Attribute objects. 285 */ 286 private void writeObject(java.io.ObjectOutputStream s) 287 throws java.io.IOException { 288 s.defaultWriteObject(); // write out the ignoreCase flag 289 s.writeInt(attrs.size()); 290 Enumeration<Attribute> attrEnum = attrs.elements(); 291 while (attrEnum.hasMoreElements()) { 292 s.writeObject(attrEnum.nextElement()); 293 } 294 } 295 296 /** 297 * Overridden to avoid exposing implementation details. 298 */ 299 private void readObject(java.io.ObjectInputStream s) 300 throws java.io.IOException, ClassNotFoundException { 301 s.defaultReadObject(); // read in the ignoreCase flag 302 int n = s.readInt(); // number of attributes 303 attrs = (n >= 1) 304 ? new Hashtable<String,Attribute>(n * 2) 305 : new Hashtable<String,Attribute>(2); // can't have initial size of 0 (grrr...) 306 while (--n >= 0) { 307 put((Attribute)s.readObject()); 308 } 309 } 310 311 312 class AttrEnumImpl implements NamingEnumeration<Attribute> { 313 314 Enumeration<Attribute> elements; 315 316 public AttrEnumImpl() { 317 this.elements = attrs.elements(); 318 } 319 320 public boolean hasMoreElements() { 321 return elements.hasMoreElements(); 322 } 323 324 public Attribute nextElement() { 325 return elements.nextElement(); 326 } 327 328 public boolean hasMore() throws NamingException { 329 return hasMoreElements(); 330 } 331 332 public Attribute next() throws NamingException { 333 return nextElement(); 334 } 335 336 public void close() throws NamingException { 337 elements = null; 338 } 339 } 340 341 class IDEnumImpl implements NamingEnumeration<String> { 342 343 Enumeration<Attribute> elements; 344 345 public IDEnumImpl() { 346 // Walking through the elements, rather than the keys, gives 347 // us attribute IDs that have not been converted to lowercase. 348 this.elements = attrs.elements(); 349 } 350 351 public boolean hasMoreElements() { 352 return elements.hasMoreElements(); 353 } 354 355 public String nextElement() { 356 Attribute attr = elements.nextElement(); 357 return attr.getID(); 358 } 359 360 public boolean hasMore() throws NamingException { 361 return hasMoreElements(); 362 } 363 364 public String next() throws NamingException { 365 return nextElement(); 366 } 367 368 public void close() throws NamingException { 369 elements = null; 370 } 371 } 372 373 /** 374 * Use serialVersionUID from JNDI 1.1.1 for interoperability. 375 */ 376 private static final long serialVersionUID = 4980164073184639448L; 377 }