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