blob: cd2a925a66046f3119f6c0690afb6a3c62114678 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1999-2004 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27package javax.naming.directory;
28
29import java.util.Hashtable;
30import java.util.Enumeration;
31
32import javax.naming.NamingException;
33import 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
70public 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 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 public Object clone() {
142 BasicAttributes attrset;
143 try {
144 attrset = (BasicAttributes)super.clone();
145 } catch (CloneNotSupportedException e) {
146 attrset = new BasicAttributes(ignoreCase);
147 }
148 attrset.attrs = (Hashtable)attrs.clone();
149 return attrset;
150 }
151
152 public boolean isCaseIgnored() {
153 return ignoreCase;
154 }
155
156 public int size() {
157 return attrs.size();
158 }
159
160 public Attribute get(String attrID) {
161 Attribute attr = (Attribute) attrs.get(
162 ignoreCase ? attrID.toLowerCase() : attrID);
163 return (attr);
164 }
165
166 public NamingEnumeration<Attribute> getAll() {
167 return new AttrEnumImpl();
168 }
169
170 public NamingEnumeration<String> getIDs() {
171 return new IDEnumImpl();
172 }
173
174 public Attribute put(String attrID, Object val) {
175 return this.put(new BasicAttribute(attrID, val));
176 }
177
178 public Attribute put(Attribute attr) {
179 String id = attr.getID();
180 if (ignoreCase) {
181 id = id.toLowerCase();
182 }
183 return (Attribute)attrs.put(id, attr);
184 }
185
186 public Attribute remove(String attrID) {
187 String id = (ignoreCase ? attrID.toLowerCase() : attrID);
188 return (Attribute)attrs.remove(id);
189 }
190
191 /**
192 * Generates the string representation of this attribute set.
193 * The string consists of each attribute identifier and the contents
194 * of each attribute. The contents of this string is useful
195 * for debugging and is not meant to be interpreted programmatically.
196 *
197 * @return A non-null string listing the contents of this attribute set.
198 */
199 public String toString() {
200 if (attrs.size() == 0) {
201 return("No attributes");
202 } else {
203 return attrs.toString();
204 }
205 }
206
207 /**
208 * Determines whether this <tt>BasicAttributes</tt> is equal to another
209 * <tt>Attributes</tt>
210 * Two <tt>Attributes</tt> are equal if they are both instances of
211 * <tt>Attributes</tt>,
212 * treat the case of attribute IDs the same way, and contain the
213 * same attributes. Each <tt>Attribute</tt> in this <tt>BasicAttributes</tt>
214 * is checked for equality using <tt>Object.equals()</tt>, which may have
215 * be overridden by implementations of <tt>Attribute</tt>).
216 * If a subclass overrides <tt>equals()</tt>,
217 * it should override <tt>hashCode()</tt>
218 * as well so that two <tt>Attributes</tt> instances that are equal
219 * have the same hash code.
220 * @param obj the possibly null object to compare against.
221 *
222 * @return true If obj is equal to this BasicAttributes.
223 * @see #hashCode
224 */
225 public boolean equals(Object obj) {
226 if ((obj != null) && (obj instanceof Attributes)) {
227 Attributes target = (Attributes)obj;
228
229 // Check case first
230 if (ignoreCase != target.isCaseIgnored()) {
231 return false;
232 }
233
234 if (size() == target.size()) {
235 Attribute their, mine;
236 try {
237 NamingEnumeration theirs = target.getAll();
238 while (theirs.hasMore()) {
239 their = (Attribute)theirs.next();
240 mine = get(their.getID());
241 if (!their.equals(mine)) {
242 return false;
243 }
244 }
245 } catch (NamingException e) {
246 return false;
247 }
248 return true;
249 }
250 }
251 return false;
252 }
253
254 /**
255 * Calculates the hash code of this BasicAttributes.
256 *<p>
257 * The hash code is computed by adding the hash code of
258 * the attributes of this object. If this BasicAttributes
259 * ignores case of its attribute IDs, one is added to the hash code.
260 * If a subclass overrides <tt>hashCode()</tt>,
261 * it should override <tt>equals()</tt>
262 * as well so that two <tt>Attributes</tt> instances that are equal
263 * have the same hash code.
264 *
265 * @return an int representing the hash code of this BasicAttributes instance.
266 * @see #equals
267 */
268 public int hashCode() {
269 int hash = (ignoreCase ? 1 : 0);
270 try {
271 NamingEnumeration all = getAll();
272 while (all.hasMore()) {
273 hash += all.next().hashCode();
274 }
275 } catch (NamingException e) {}
276 return hash;
277 }
278
279 /**
280 * Overridden to avoid exposing implementation details.
281 * @serialData Default field (ignoreCase flag -- a boolean), followed by
282 * the number of attributes in the set
283 * (an int), and then the individual Attribute objects.
284 */
285 private void writeObject(java.io.ObjectOutputStream s)
286 throws java.io.IOException {
287 s.defaultWriteObject(); // write out the ignoreCase flag
288 s.writeInt(attrs.size());
289 Enumeration attrEnum = attrs.elements();
290 while (attrEnum.hasMoreElements()) {
291 s.writeObject(attrEnum.nextElement());
292 }
293 }
294
295 /**
296 * Overridden to avoid exposing implementation details.
297 */
298 private void readObject(java.io.ObjectInputStream s)
299 throws java.io.IOException, ClassNotFoundException {
300 s.defaultReadObject(); // read in the ignoreCase flag
301 int n = s.readInt(); // number of attributes
302 attrs = (n >= 1)
303 ? new Hashtable(n * 2)
304 : new Hashtable(2); // can't have initial size of 0 (grrr...)
305 while (--n >= 0) {
306 put((Attribute)s.readObject());
307 }
308 }
309
310
311class AttrEnumImpl implements NamingEnumeration<Attribute> {
312
313 Enumeration<Attribute> elements;
314
315 public AttrEnumImpl() {
316 this.elements = attrs.elements();
317 }
318
319 public boolean hasMoreElements() {
320 return elements.hasMoreElements();
321 }
322
323 public Attribute nextElement() {
324 return elements.nextElement();
325 }
326
327 public boolean hasMore() throws NamingException {
328 return hasMoreElements();
329 }
330
331 public Attribute next() throws NamingException {
332 return nextElement();
333 }
334
335 public void close() throws NamingException {
336 elements = null;
337 }
338}
339
340class IDEnumImpl implements NamingEnumeration<String> {
341
342 Enumeration<Attribute> elements;
343
344 public IDEnumImpl() {
345 // Walking through the elements, rather than the keys, gives
346 // us attribute IDs that have not been converted to lowercase.
347 this.elements = attrs.elements();
348 }
349
350 public boolean hasMoreElements() {
351 return elements.hasMoreElements();
352 }
353
354 public String nextElement() {
355 Attribute attr = elements.nextElement();
356 return attr.getID();
357 }
358
359 public boolean hasMore() throws NamingException {
360 return hasMoreElements();
361 }
362
363 public String next() throws NamingException {
364 return nextElement();
365 }
366
367 public void close() throws NamingException {
368 elements = null;
369 }
370}
371
372 /**
373 * Use serialVersionUID from JNDI 1.1.1 for interoperability.
374 */
375 private static final long serialVersionUID = 4980164073184639448L;
376}