1. /*
  2. * @(#)BasicAttribute.java 1.11 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.Vector;
  9. import java.util.Enumeration;
  10. import java.util.NoSuchElementException;
  11. import java.lang.reflect.Array;
  12. import javax.naming.NamingException;
  13. import javax.naming.NamingEnumeration;
  14. import javax.naming.OperationNotSupportedException;
  15. /**
  16. * This class provides a basic implementation of the <tt>Attribute</tt> interface.
  17. *<p>
  18. * This implementation does not support the schema methods
  19. * <tt>getAttributeDefinition()</tt> and <tt>getAttributeSyntaxDefinition()</tt>.
  20. * They simply throw <tt>OperationNotSupportedException</tt>.
  21. * Subclasses of <tt>BasicAttribute</tt> should override these methods if they
  22. * support them.
  23. *<p>
  24. * The <tt>BasicAttribute</tt> class by default uses <tt>Object.equals()</tt> to
  25. * determine equality of attribute values when testing for equality or
  26. * when searching for values, <em>except</em> when the value is an array.
  27. * For an array, each element of the array is checked using <tt>Object.equals()</tt>.
  28. * Subclasses of <tt>BasicAttribute</tt> can make use of schema information
  29. * when doing similar equality checks by overriding methods
  30. * in which such use of schema is meaningful.
  31. * Similarly, the <tt>BasicAttribute</tt> class by default returns the values passed to its
  32. * constructor and/or manipulated using the add/remove methods.
  33. * Subclasses of <tt>BasicAttribute</tt> can override <tt>get()</tt> and <tt>getAll()</tt>
  34. * to get the values dynamically from the directory (or implement
  35. * the <tt>Attribute</tt> interface directly instead of subclassing <tt>BasicAttribute</tt>).
  36. *<p>
  37. * Note that updates to <tt>BasicAttribute</tt> (such as adding or removing a value)
  38. * does not affect the corresponding representation of the attribute
  39. * in the directory. Updates to the directory can only be effected
  40. * using operations in the <tt>DirContext</tt> interface.
  41. *<p>
  42. * A <tt>BasicAttribute</tt> instance is not synchronized against concurrent
  43. * multithreaded access. Multiple threads trying to access and modify a
  44. * <tt>BasicAttribute</tt> should lock the object.
  45. *
  46. * @author Rosanna Lee
  47. * @author Scott Seligman
  48. * @version 1.11 03/01/23
  49. * @since 1.3
  50. */
  51. public class BasicAttribute implements Attribute {
  52. /**
  53. * Holds the attribute's id. It is initialized by the public constructor and
  54. * cannot be null unless methods in BasicAttribute that use attrID
  55. * have been overridden.
  56. * @serial
  57. */
  58. protected String attrID;
  59. /**
  60. * Holds the attribute's values. Initialized by public constructors.
  61. * Cannot be null unless methods in BasicAttribute that use
  62. * values have been overridden.
  63. */
  64. protected transient Vector values;
  65. /**
  66. * A flag for recording whether this attribute's values are ordered.
  67. * @serial
  68. */
  69. protected boolean ordered = false;
  70. public Object clone() {
  71. BasicAttribute attr;
  72. try {
  73. attr = (BasicAttribute)super.clone();
  74. } catch (CloneNotSupportedException e) {
  75. attr = new BasicAttribute(attrID, ordered);
  76. }
  77. attr.values = (Vector)values.clone();
  78. return attr;
  79. }
  80. /**
  81. * Determines whether obj is equal to this attribute.
  82. * Two attributes are equal if their attribute-ids, syntaxes
  83. * and values are equal.
  84. * If the attribute values are unordered, the order that the values were added
  85. * are irrelevant. If the attribute values are ordered, then the
  86. * order the values must match.
  87. * If obj is null or not an Attribute, false is returned.
  88. *<p>
  89. * By default <tt>Object.equals()</tt> is used when comparing the attribute
  90. * id and its values except when a value is an array. For an array,
  91. * each element of the array is checked using <tt>Object.equals()</tt>.
  92. * A subclass may override this to make
  93. * use of schema syntax information and matching rules,
  94. * which define what it means for two attributes to be equal.
  95. * How and whether a subclass makes
  96. * use of the schema information is determined by the subclass.
  97. * If a subclass overrides <tt>equals()</tt>, it should also override
  98. * <tt>hashCode()</tt>
  99. * such that two attributes that are equal have the same hash code.
  100. *
  101. * @param obj The possibly null object to check.
  102. * @return true if obj is equal to this attribute; false otherwise.
  103. * @see #hashCode
  104. * @see #contains
  105. */
  106. public boolean equals(Object obj) {
  107. if ((obj != null) && (obj instanceof Attribute)) {
  108. Attribute target = (Attribute)obj;
  109. // Check order first
  110. if (isOrdered() != target.isOrdered()) {
  111. return false;
  112. }
  113. int len;
  114. if (attrID.equals(target.getID()) &&
  115. (len=size()) == target.size()) {
  116. try {
  117. if (isOrdered()) {
  118. // Go through both list of values
  119. for (int i = 0; i < len; i++) {
  120. if (!valueEquals(get(i), target.get(i))) {
  121. return false;
  122. }
  123. }
  124. } else {
  125. // order is not relevant; check for existence
  126. Enumeration theirs = target.getAll();
  127. while (theirs.hasMoreElements()) {
  128. if (find(theirs.nextElement()) < 0)
  129. return false;
  130. }
  131. }
  132. } catch (NamingException e) {
  133. return false;
  134. }
  135. return true;
  136. }
  137. }
  138. return false;
  139. }
  140. /**
  141. * Calculates the hash code of this attribute.
  142. *<p>
  143. * The hash code is computed by adding the hash code of
  144. * the attribute's id and that of all of its values except for
  145. * values that are arrays.
  146. * For an array, the hash code of each element of the array is summed.
  147. * If a subclass overrides <tt>hashCode()</tt>, it should override
  148. * <tt>equals()</tt>
  149. * as well so that two attributes that are equal have the same hash code.
  150. *
  151. * @return an int representing the hash code of this attribute.
  152. * @see #equals
  153. */
  154. public int hashCode() {
  155. int hash = attrID.hashCode();
  156. int num = values.size();
  157. Object val;
  158. for (int i = 0; i < num; i ++) {
  159. val = values.elementAt(i);
  160. if (val != null) {
  161. if (val.getClass().isArray()) {
  162. Object it;
  163. int len = Array.getLength(val);
  164. for (int j = 0 ; j < len ; j++) {
  165. it = Array.get(val, j);
  166. if (it != null) {
  167. hash += it.hashCode();
  168. }
  169. }
  170. } else {
  171. hash += val.hashCode();
  172. }
  173. }
  174. }
  175. return hash;
  176. }
  177. /**
  178. * Generates the string representation of this attribute.
  179. * The string consists of the attribute's id and its values.
  180. * This string is meant for debugging and not meant to be
  181. * interpreted programmatically.
  182. * @return The non-null string representation of this attribute.
  183. */
  184. public String toString() {
  185. StringBuffer answer = new StringBuffer(attrID + ": ");
  186. if (values.size() == 0) {
  187. answer.append("No values");
  188. } else {
  189. boolean start = true;
  190. for (Enumeration e = values.elements(); e.hasMoreElements(); ) {
  191. if (!start)
  192. answer.append(", ");
  193. answer.append(e.nextElement());
  194. start = false;
  195. }
  196. }
  197. return answer.toString();
  198. }
  199. /**
  200. * Constructs a new instance of an unordered attribute with no value.
  201. *
  202. * @param id The attribute's id. It cannot be null.
  203. */
  204. public BasicAttribute(String id) {
  205. this(id, false);
  206. }
  207. /**
  208. * Constructs a new instance of an unordered attribute with a single value.
  209. *
  210. * @param id The attribute's id. It cannot be null.
  211. * @param value The attribute's value. If null, a null
  212. * value is added to the attribute.
  213. */
  214. public BasicAttribute(String id, Object value) {
  215. this(id, value, false);
  216. }
  217. /**
  218. * Constructs a new instance of a possibly ordered attribute with no value.
  219. *
  220. * @param id The attribute's id. It cannot be null.
  221. * @param ordered true means the attribute's values will be ordered;
  222. * false otherwise.
  223. */
  224. public BasicAttribute(String id, boolean ordered) {
  225. attrID = id;
  226. values = new Vector();
  227. this.ordered = ordered;
  228. }
  229. /**
  230. * Constructs a new instance of a possibly ordered attribute with a
  231. * single value.
  232. *
  233. * @param id The attribute's id. It cannot be null.
  234. * @param value The attribute's value. If null, a null
  235. * value is added to the attribute.
  236. * @param ordered true means the attribute's values will be ordered;
  237. * false otherwise.
  238. */
  239. public BasicAttribute(String id, Object value, boolean ordered) {
  240. this(id, ordered);
  241. values.addElement(value);
  242. }
  243. /**
  244. * Retrieves an enumeration of this attribute's values.
  245. *<p>
  246. * By default, the values returned are those passed to the
  247. * constructor and/or manipulated using the add/replace/remove methods.
  248. * A subclass may override this to retrieve the values dynamically
  249. * from the directory.
  250. */
  251. public NamingEnumeration getAll() throws NamingException {
  252. return new ValuesEnumImpl();
  253. }
  254. /**
  255. * Retrieves one of this attribute's values.
  256. *<p>
  257. * By default, the value returned is one of those passed to the
  258. * constructor and/or manipulated using the add/replace/remove methods.
  259. * A subclass may override this to retrieve the value dynamically
  260. * from the directory.
  261. */
  262. public Object get() throws NamingException {
  263. if (values.size() == 0) {
  264. throw new
  265. NoSuchElementException("Attribute " + getID() + " has no value");
  266. } else {
  267. return values.elementAt(0);
  268. }
  269. }
  270. public int size() {
  271. return values.size();
  272. }
  273. public String getID() {
  274. return attrID;
  275. }
  276. /**
  277. * Determines whether a value is in this attribute.
  278. *<p>
  279. * By default,
  280. * <tt>Object.equals()</tt> is used when comparing <tt>attrVal</tt>
  281. * with this attribute's values except when <tt>attrVal</tt> is an array.
  282. * For an array, each element of the array is checked using
  283. * <tt>Object.equals()</tt>.
  284. * A subclass may use schema information to determine equality.
  285. */
  286. public boolean contains(Object attrVal) {
  287. return (find(attrVal) >= 0);
  288. }
  289. // For finding first element that has a null in JDK1.1 Vector.
  290. // In the Java 2 platform, can just replace this with Vector.indexOf(target);
  291. private int find(Object target) {
  292. Class cl;
  293. if (target == null) {
  294. int ct = values.size();
  295. for (int i = 0 ; i < ct ; i++) {
  296. if (values.elementAt(i) == null)
  297. return i;
  298. }
  299. } else if ((cl=target.getClass()).isArray()) {
  300. int ct = values.size();
  301. Object it;
  302. for (int i = 0 ; i < ct ; i++) {
  303. it = values.elementAt(i);
  304. if (it != null && cl == it.getClass()
  305. && arrayEquals(target, it))
  306. return i;
  307. }
  308. } else {
  309. return values.indexOf(target, 0);
  310. }
  311. return -1; // not found
  312. }
  313. /**
  314. * Determines whether two attribute values are equal.
  315. * Use arrayEquals for arrays and <tt>Object.equals()</tt> otherwise.
  316. */
  317. private static boolean valueEquals(Object obj1, Object obj2) {
  318. if (obj1 == obj2) {
  319. return true; // object references are equal
  320. }
  321. if (obj1 == null) {
  322. return false; // obj2 was not false
  323. }
  324. if (obj1.getClass().isArray() &&
  325. obj2.getClass().isArray()) {
  326. return arrayEquals(obj1, obj2);
  327. }
  328. return (obj1.equals(obj2));
  329. }
  330. /**
  331. * Determines whether two arrays are equal by comparing each of their
  332. * elements using <tt>Object.equals()</tt>.
  333. */
  334. private static boolean arrayEquals(Object a1, Object a2) {
  335. int len;
  336. if ((len = Array.getLength(a1)) != Array.getLength(a2))
  337. return false;
  338. for (int j = 0; j < len; j++) {
  339. Object i1 = Array.get(a1, j);
  340. Object i2 = Array.get(a2, j);
  341. if (i1 == null || i2 == null) {
  342. if (i1 != i2)
  343. return false;
  344. } else if (!i1.equals(i2)) {
  345. return false;
  346. }
  347. }
  348. return true;
  349. }
  350. /**
  351. * Adds a new value to this attribute.
  352. *<p>
  353. * By default, <tt>Object.equals()</tt> is used when comparing <tt>attrVal</tt>
  354. * with this attribute's values except when <tt>attrVal</tt> is an array.
  355. * For an array, each element of the array is checked using
  356. * <tt>Object.equals()</tt>.
  357. * A subclass may use schema information to determine equality.
  358. */
  359. public boolean add(Object attrVal) {
  360. if (isOrdered() || (find(attrVal) < 0)) {
  361. values.addElement(attrVal);
  362. return true;
  363. } else {
  364. return false;
  365. }
  366. }
  367. /**
  368. * Removes a specified value from this attribute.
  369. *<p>
  370. * By default, <tt>Object.equals()</tt> is used when comparing <tt>attrVal</tt>
  371. * with this attribute's values except when <tt>attrVal</tt> is an array.
  372. * For an array, each element of the array is checked using
  373. * <tt>Object.equals()</tt>.
  374. * A subclass may use schema information to determine equality.
  375. */
  376. public boolean remove(Object attrval) {
  377. // For the Java 2 platform, can just use "return removeElement(attrval);"
  378. // Need to do the following to handle null case
  379. int i = find(attrval);
  380. if (i >= 0) {
  381. values.removeElementAt(i);
  382. return true;
  383. }
  384. return false;
  385. }
  386. public void clear() {
  387. values.setSize(0);
  388. }
  389. // ---- ordering methods
  390. public boolean isOrdered() {
  391. return ordered;
  392. }
  393. public Object get(int ix) throws NamingException {
  394. return values.elementAt(ix);
  395. }
  396. public Object remove(int ix) {
  397. Object answer = values.elementAt(ix);
  398. values.removeElementAt(ix);
  399. return answer;
  400. }
  401. public void add(int ix, Object attrVal) {
  402. if (!isOrdered() && contains(attrVal)) {
  403. throw new IllegalStateException(
  404. "Cannot add duplicate to unordered attribute");
  405. }
  406. values.insertElementAt(attrVal, ix);
  407. }
  408. public Object set(int ix, Object attrVal) {
  409. if (!isOrdered() && contains(attrVal)) {
  410. throw new IllegalStateException(
  411. "Cannot add duplicate to unordered attribute");
  412. }
  413. Object answer = values.elementAt(ix);
  414. values.setElementAt(attrVal, ix);
  415. return answer;
  416. }
  417. // ----------------- Schema methods
  418. /**
  419. * Retrieves the syntax definition associated with this attribute.
  420. *<p>
  421. * This method by default throws OperationNotSupportedException. A subclass
  422. * should override this method if it supports schema.
  423. */
  424. public DirContext getAttributeSyntaxDefinition() throws NamingException {
  425. throw new OperationNotSupportedException("attribute syntax");
  426. }
  427. /**
  428. * Retrieves this attribute's schema definition.
  429. *<p>
  430. * This method by default throws OperationNotSupportedException. A subclass
  431. * should override this method if it supports schema.
  432. */
  433. public DirContext getAttributeDefinition() throws NamingException {
  434. throw new OperationNotSupportedException("attribute definition");
  435. }
  436. // ---- serialization methods
  437. /**
  438. * Overridden to avoid exposing implementation details
  439. * @serialData Default field (the attribute ID -- a String),
  440. * followed by the number of values (an int), and the
  441. * individual values.
  442. */
  443. private void writeObject(java.io.ObjectOutputStream s)
  444. throws java.io.IOException {
  445. s.defaultWriteObject(); // write out the attrID
  446. s.writeInt(values.size());
  447. for (int i = 0; i < values.size(); i++) {
  448. s.writeObject(values.elementAt(i));
  449. }
  450. }
  451. /**
  452. * Overridden to avoid exposing implementation details.
  453. */
  454. private void readObject(java.io.ObjectInputStream s)
  455. throws java.io.IOException, ClassNotFoundException {
  456. s.defaultReadObject(); // read in the attrID
  457. int n = s.readInt(); // number of values
  458. values = new Vector(n);
  459. while (--n >= 0) {
  460. values.addElement(s.readObject());
  461. }
  462. }
  463. class ValuesEnumImpl implements NamingEnumeration {
  464. Enumeration list;
  465. ValuesEnumImpl() {
  466. list = values.elements();
  467. }
  468. public boolean hasMoreElements() {
  469. return list.hasMoreElements();
  470. }
  471. public Object nextElement() {
  472. return(list.nextElement());
  473. }
  474. public Object next() throws NamingException {
  475. return list.nextElement();
  476. }
  477. public boolean hasMore() throws NamingException {
  478. return list.hasMoreElements();
  479. }
  480. public void close() throws NamingException {
  481. list = null;
  482. }
  483. }
  484. /**
  485. * Use serialVersionUID from JNDI 1.1.1 for interoperability.
  486. */
  487. private static final long serialVersionUID = 6743528196119291326L;
  488. }