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