001 /*
002 * Copyright 1999-2004 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.naming.directory;
027
028 import java.util.Hashtable;
029 import java.util.Enumeration;
030
031 import javax.naming.NamingException;
032 import javax.naming.NamingEnumeration;
033
034 /**
035 * This class provides a basic implementation
036 * of the Attributes interface.
037 *<p>
038 * BasicAttributes is either case-sensitive or case-insensitive (case-ignore).
039 * This property is determined at the time the BasicAttributes constructor
040 * is called.
041 * In a case-insensitive BasicAttributes, the case of its attribute identifiers
042 * is ignored when searching for an attribute, or adding attributes.
043 * In a case-sensitive BasicAttributes, the case is significant.
044 *<p>
045 * When the BasicAttributes class needs to create an Attribute, it
046 * uses BasicAttribute. There is no other dependency on BasicAttribute.
047 *<p>
048 * Note that updates to BasicAttributes (such as adding or removing an attribute)
049 * does not affect the corresponding representation in the directory.
050 * Updates to the directory can only be effected
051 * using operations in the DirContext interface.
052 *<p>
053 * A BasicAttributes instance is not synchronized against concurrent
054 * multithreaded access. Multiple threads trying to access and modify
055 * a single BasicAttributes instance should lock the object.
056 *
057 * @author Rosanna Lee
058 * @author Scott Seligman
059 * @version 1.19 07/05/05
060 *
061 * @see DirContext#getAttributes
062 * @see DirContext#modifyAttributes
063 * @see DirContext#bind
064 * @see DirContext#rebind
065 * @see DirContext#createSubcontext
066 * @see DirContext#search
067 * @since 1.3
068 */
069
070 public class BasicAttributes implements Attributes {
071 /**
072 * Indicates whether case of attribute ids is ignored.
073 * @serial
074 */
075 private boolean ignoreCase = false;
076
077 // The 'key' in attrs is stored in the 'right case'.
078 // If ignoreCase is true, key is aways lowercase.
079 // If ignoreCase is false, key is stored as supplied by put().
080 // %%% Not declared "private" due to bug 4064984.
081 transient Hashtable attrs = new Hashtable(11);
082
083 /**
084 * Constructs a new instance of Attributes.
085 * The character case of attribute identifiers
086 * is significant when subsequently retrieving or adding attributes.
087 */
088 public BasicAttributes() {
089 }
090
091 /**
092 * Constructs a new instance of Attributes.
093 * If <code>ignoreCase</code> is true, the character case of attribute
094 * identifiers is ignored; otherwise the case is significant.
095 * @param ignoreCase true means this attribute set will ignore
096 * the case of its attribute identifiers
097 * when retrieving or adding attributes;
098 * false means case is respected.
099 */
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(ignoreCase ? attrID
162 .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 (Attribute) 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 }
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 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) ? new Hashtable(n * 2) : new Hashtable(2); // can't have initial size of 0 (grrr...)
304 while (--n >= 0) {
305 put((Attribute) s.readObject());
306 }
307 }
308
309 class AttrEnumImpl implements NamingEnumeration<Attribute> {
310
311 Enumeration<Attribute> elements;
312
313 public AttrEnumImpl() {
314 this .elements = attrs.elements();
315 }
316
317 public boolean hasMoreElements() {
318 return elements.hasMoreElements();
319 }
320
321 public Attribute nextElement() {
322 return elements.nextElement();
323 }
324
325 public boolean hasMore() throws NamingException {
326 return hasMoreElements();
327 }
328
329 public Attribute next() throws NamingException {
330 return nextElement();
331 }
332
333 public void close() throws NamingException {
334 elements = null;
335 }
336 }
337
338 class IDEnumImpl implements NamingEnumeration<String> {
339
340 Enumeration<Attribute> elements;
341
342 public IDEnumImpl() {
343 // Walking through the elements, rather than the keys, gives
344 // us attribute IDs that have not been converted to lowercase.
345 this .elements = attrs.elements();
346 }
347
348 public boolean hasMoreElements() {
349 return elements.hasMoreElements();
350 }
351
352 public String nextElement() {
353 Attribute attr = (Attribute) elements.nextElement();
354 return attr.getID();
355 }
356
357 public boolean hasMore() throws NamingException {
358 return hasMoreElements();
359 }
360
361 public String next() throws NamingException {
362 return nextElement();
363 }
364
365 public void close() throws NamingException {
366 elements = null;
367 }
368 }
369
370 /**
371 * Use serialVersionUID from JNDI 1.1.1 for interoperability.
372 */
373 private static final long serialVersionUID = 4980164073184639448L;
374 }
|