- /*
- * @(#)BasicAttribute.java 1.9 01/02/09
- *
- * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
- *
- * This software is the proprietary information of Sun Microsystems, Inc.
- * Use is subject to license terms.
- *
- */
-
- package javax.naming.directory;
-
- import java.util.Vector;
- import java.util.Enumeration;
- import java.util.NoSuchElementException;
- import java.lang.reflect.Array;
-
- import javax.naming.NamingException;
- import javax.naming.NamingEnumeration;
- import javax.naming.OperationNotSupportedException;
-
- /**
- * This class provides a basic implementation of the <tt>Attribute</tt> interface.
- *<p>
- * This implementation does not support the schema methods
- * <tt>getAttributeDefinition()</tt> and <tt>getAttributeSyntaxDefinition()</tt>.
- * They simply throw <tt>OperationNotSupportedException</tt>.
- * Subclasses of <tt>BasicAttribute</tt> should override these methods if they
- * support them.
- *<p>
- * The <tt>BasicAttribute</tt> class by default uses <tt>Object.equals()</tt> to
- * determine equality of attribute values when testing for equality or
- * when searching for values, <em>except</em> when the value is an array.
- * For an array, each element of the array is checked using <tt>Object.equals()</tt>.
- * Subclasses of <tt>BasicAttribute</tt> can make use of schema information
- * when doing similar equality checks by overriding methods
- * in which such use of schema is meaningful.
- * Similarly, the <tt>BasicAttribute</tt> class by default returns the values passed to its
- * constructor and/or manipulated using the add/remove methods.
- * Subclasses of <tt>BasicAttribute</tt> can override <tt>get()</tt> and <tt>getAll()</tt>
- * to get the values dynamically from the directory (or implement
- * the <tt>Attribute</tt> interface directly instead of subclassing <tt>BasicAttribute</tt>).
- *<p>
- * Note that updates to <tt>BasicAttribute</tt> (such as adding or removing a value)
- * does not affect the corresponding representation of the attribute
- * in the directory. Updates to the directory can only be effected
- * using operations in the <tt>DirContext</tt> interface.
- *<p>
- * A <tt>BasicAttribute</tt> instance is not synchronized against concurrent
- * multithreaded access. Multiple threads trying to access and modify a
- * <tt>BasicAttribute</tt> should lock the object.
- *
- * @author Rosanna Lee
- * @author Scott Seligman
- * @version 1.9 01/02/09
- * @since 1.3
- */
- public class BasicAttribute implements Attribute {
- /**
- * Holds the attribute's id. It is initialized by the public constructor and
- * cannot be null unless methods in BasicAttribute that use attrID
- * have been overridden.
- * @serial
- */
- protected String attrID;
-
- /**
- * Holds the attribute's values. Initialized by public constructors.
- * Cannot be null unless methods in BasicAttribute that use
- * values have been overridden.
- */
- protected transient Vector values;
-
- /**
- * A flag for recording whether this attribute's values are ordered.
- * @serial
- */
- protected boolean ordered = false;
-
- public Object clone() {
- BasicAttribute attr;
- try {
- attr = (BasicAttribute)super.clone();
- } catch (CloneNotSupportedException e) {
- attr = new BasicAttribute(attrID, ordered);
- }
- attr.values = (Vector)values.clone();
- return attr;
- }
-
- /**
- * Determines whether obj is equal to this attribute.
- * Two attributes are equal if their attribute-ids, syntaxes
- * and values are equal.
- * If the attribute values are unordered, the order that the values were added
- * are irrelevant. If the attribute values are ordered, then the
- * order the values must match.
- * If obj is null or not an Attribute, false is returned.
- *<p>
- * By default <tt>Object.equals()</tt> is used when comparing the attribute
- * id and its values except when a value is an array. For an array,
- * each element of the array is checked using <tt>Object.equals()</tt>.
- * A subclass may override this to make
- * use of schema syntax information and matching rules,
- * which define what it means for two attributes to be equal.
- * How and whether a subclass makes
- * use of the schema information is determined by the subclass.
- * If a subclass overrides <tt>equals()</tt>, it should also override
- * <tt>hashCode()</tt>
- * such that two attributes that are equal have the same hash code.
- *
- * @param obj The possibly null object to check.
- * @return true if obj is equal to this attribute; false otherwise.
- * @see #hashCode
- * @see #contains
- */
- public boolean equals(Object obj) {
- if ((obj != null) && (obj instanceof Attribute)) {
- Attribute target = (Attribute)obj;
-
- // Check order first
- if (isOrdered() != target.isOrdered()) {
- return false;
- }
- int len;
- if (attrID.equals(target.getID()) &&
- (len=size()) == target.size()) {
- try {
- if (isOrdered()) {
- // Go through both list of values
- for (int i = 0; i < len; i++) {
- if (!valueEquals(get(i), target.get(i))) {
- return false;
- }
- }
- } else {
- // order is not relevant; check for existence
- Enumeration theirs = target.getAll();
- while (theirs.hasMoreElements()) {
- if (find(theirs.nextElement()) < 0)
- return false;
- }
- }
- } catch (NamingException e) {
- return false;
- }
- return true;
- }
- }
- return false;
- }
-
- /**
- * Calculates the hash code of this attribute.
- *<p>
- * The hash code is computed by adding the hash code of
- * the attribute's id and that of all of its values except for
- * values that are arrays.
- * For an array, the hash code of each element of the array is summed.
- * If a subclass overrides <tt>hashCode()</tt>, it should override
- * <tt>equals()</tt>
- * as well so that two attributes that are equal have the same hash code.
- *
- * @return an int representing the hash code of this attribute.
- * @see #equals
- */
- public int hashCode() {
- int hash = attrID.hashCode();
- int num = values.size();
- Object val;
- for (int i = 0; i < num; i ++) {
- val = values.elementAt(i);
- if (val != null) {
- if (val.getClass().isArray()) {
- Object it;
- int len = Array.getLength(val);
- for (int j = 0 ; j < len ; j++) {
- it = Array.get(val, j);
- if (it != null) {
- hash += it.hashCode();
- }
- }
- } else {
- hash += val.hashCode();
- }
- }
- }
- return hash;
- }
-
- /**
- * Generates the string representation of this attribute.
- * The string consists of the attribute's id and its values.
- * This string is meant for debugging and not meant to be
- * interpreted programmatically.
- * @return The non-null string representation of this attribute.
- */
- public String toString() {
- StringBuffer answer = new StringBuffer(attrID + ": ");
- if (values.size() == 0) {
- answer.append("No values");
- } else {
- boolean start = true;
- for (Enumeration e = values.elements(); e.hasMoreElements(); ) {
- if (!start)
- answer.append(", ");
- answer.append(e.nextElement());
- start = false;
- }
- }
- return answer.toString();
- }
-
- /**
- * Constructs a new instance of an unordered attribute with no value.
- *
- * @param id The attribute's id. It cannot be null.
- */
- public BasicAttribute(String id) {
- this(id, false);
- }
-
- /**
- * Constructs a new instance of an unordered attribute with a single value.
- *
- * @param id The attribute's id. It cannot be null.
- * @param value The attribute's value. If null, a null
- * value is added to the attribute.
- */
- public BasicAttribute(String id, Object value) {
- this(id, value, false);
- }
-
- /**
- * Constructs a new instance of a possibly ordered attribute with no value.
- *
- * @param id The attribute's id. It cannot be null.
- * @param ordered true means the attribute's values will be ordered;
- * false otherwise.
- */
- public BasicAttribute(String id, boolean ordered) {
- attrID = id;
- values = new Vector();
- this.ordered = ordered;
- }
-
- /**
- * Constructs a new instance of a possibly ordered attribute with a
- * single value.
- *
- * @param id The attribute's id. It cannot be null.
- * @param value The attribute's value. If null, a null
- * value is added to the attribute.
- * @param ordered true means the attribute's values will be ordered;
- * false otherwise.
- */
- public BasicAttribute(String id, Object value, boolean ordered) {
- this(id, ordered);
- values.addElement(value);
- }
-
- /**
- * Retrieves an enumeration of this attribute's values.
- *<p>
- * By default, the values returned are those passed to the
- * constructor and/or manipulated using the add/replace/remove methods.
- * A subclass may override this to retrieve the values dynamically
- * from the directory.
- */
- public NamingEnumeration getAll() throws NamingException {
- return new ValuesEnumImpl();
- }
-
- /**
- * Retrieves one of this attribute's values.
- *<p>
- * By default, the value returned is one of those passed to the
- * constructor and/or manipulated using the add/replace/remove methods.
- * A subclass may override this to retrieve the value dynamically
- * from the directory.
- */
- public Object get() throws NamingException {
- if (values.size() == 0) {
- throw new
- NoSuchElementException("Attribute " + getID() + " has no value");
- } else {
- return values.elementAt(0);
- }
- }
-
- public int size() {
- return values.size();
- }
-
- public String getID() {
- return attrID;
- }
-
- /**
- * Determines whether a value is in this attribute.
- *<p>
- * By default,
- * <tt>Object.equals()</tt> is used when comparing <tt>attrVal</tt>
- * with this attribute's values except when <tt>attrVal</tt> is an array.
- * For an array, each element of the array is checked using
- * <tt>Object.equals()</tt>.
- * A subclass may use schema information to determine equality.
- */
- public boolean contains(Object attrVal) {
- return (find(attrVal) >= 0);
- }
-
- // For finding first element that has a null in JDK1.1 Vector.
- // In the Java 2 platform, can just replace this with Vector.indexOf(target);
- private int find(Object target) {
- Class cl;
- if (target == null) {
- int ct = values.size();
- for (int i = 0 ; i < ct ; i++) {
- if (values.elementAt(i) == null)
- return i;
- }
- } else if ((cl=target.getClass()).isArray()) {
- int ct = values.size();
- Object it;
- for (int i = 0 ; i < ct ; i++) {
- it = values.elementAt(i);
- if (it != null && cl == it.getClass()
- && arrayEquals(target, it))
- return i;
- }
- } else {
- return values.indexOf(target, 0);
- }
- return -1; // not found
- }
-
- /**
- * Determines whether two attribute values are equal.
- * Use arrayEquals for arrays and <tt>Object.equals()</tt> otherwise.
- */
- private static boolean valueEquals(Object obj1, Object obj2) {
- if (obj1 == obj2) {
- return true; // object references are equal
- }
- if (obj1 == null) {
- return false; // obj2 was not false
- }
- if (obj1.getClass().isArray() &&
- obj2.getClass().isArray()) {
- return arrayEquals(obj1, obj2);
- }
- return (obj1.equals(obj2));
- }
-
- /**
- * Determines whether two arrays are equal by comparing each of their
- * elements using <tt>Object.equals()</tt>.
- */
- private static boolean arrayEquals(Object a1, Object a2) {
- int len;
- if ((len = Array.getLength(a1)) != Array.getLength(a2))
- return false;
-
- for (int j = 0; j < len; j++) {
- Object i1 = Array.get(a1, j);
- Object i2 = Array.get(a2, j);
- if (i1 == null || i2 == null) {
- if (i1 != i2)
- return false;
- } else if (!i1.equals(i2)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Adds a new value to this attribute.
- *<p>
- * By default, <tt>Object.equals()</tt> is used when comparing <tt>attrVal</tt>
- * with this attribute's values except when <tt>attrVal</tt> is an array.
- * For an array, each element of the array is checked using
- * <tt>Object.equals()</tt>.
- * A subclass may use schema information to determine equality.
- */
- public boolean add(Object attrVal) {
- if (isOrdered() || (find(attrVal) < 0)) {
- values.addElement(attrVal);
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Removes a specified value from this attribute.
- *<p>
- * By default, <tt>Object.equals()</tt> is used when comparing <tt>attrVal</tt>
- * with this attribute's values except when <tt>attrVal</tt> is an array.
- * For an array, each element of the array is checked using
- * <tt>Object.equals()</tt>.
- * A subclass may use schema information to determine equality.
- */
- public boolean remove(Object attrval) {
- // For the Java 2 platform, can just use "return removeElement(attrval);"
- // Need to do the following to handle null case
-
- int i = find(attrval);
- if (i >= 0) {
- values.removeElementAt(i);
- return true;
- }
- return false;
- }
-
- public void clear() {
- values.setSize(0);
- }
-
- // ---- ordering methods
-
- public boolean isOrdered() {
- return ordered;
- }
-
- public Object get(int ix) throws NamingException {
- return values.elementAt(ix);
- }
-
- public Object remove(int ix) {
- Object answer = values.elementAt(ix);
- values.removeElementAt(ix);
- return answer;
- }
-
- public void add(int ix, Object attrVal) {
- if (!isOrdered() && contains(attrVal)) {
- throw new IllegalStateException(
- "Cannot add duplicate to unordered attribute");
- }
- values.insertElementAt(attrVal, ix);
- }
-
- public Object set(int ix, Object attrVal) {
- if (!isOrdered() && contains(attrVal)) {
- throw new IllegalStateException(
- "Cannot add duplicate to unordered attribute");
- }
-
- Object answer = values.elementAt(ix);
- values.setElementAt(attrVal, ix);
- return answer;
- }
-
- // ----------------- Schema methods
-
- /**
- * Retrieves the syntax definition associated with this attribute.
- *<p>
- * This method by default throws OperationNotSupportedException. A subclass
- * should override this method if it supports schema.
- */
- public DirContext getAttributeSyntaxDefinition() throws NamingException {
- throw new OperationNotSupportedException("attribute syntax");
- }
-
- /**
- * Retrieves this attribute's schema definition.
- *<p>
- * This method by default throws OperationNotSupportedException. A subclass
- * should override this method if it supports schema.
- */
- public DirContext getAttributeDefinition() throws NamingException {
- throw new OperationNotSupportedException("attribute definition");
- }
-
-
- // ---- serialization methods
-
- /**
- * Overriden to avoid exposing implementation details
- * @serialData Default field (the attribute ID -- a String),
- * followed by the number of values (an int), and the
- * individual values.
- */
- private void writeObject(java.io.ObjectOutputStream s)
- throws java.io.IOException {
- s.defaultWriteObject(); // write out the attrID
- s.writeInt(values.size());
- for (int i = 0; i < values.size(); i++) {
- s.writeObject(values.elementAt(i));
- }
- }
-
- /**
- * Overriden to avoid exposing implementation details.
- */
- private void readObject(java.io.ObjectInputStream s)
- throws java.io.IOException, ClassNotFoundException {
- s.defaultReadObject(); // read in the attrID
- int n = s.readInt(); // number of values
- values = new Vector(n);
- while (--n >= 0) {
- values.addElement(s.readObject());
- }
- }
-
-
- class ValuesEnumImpl implements NamingEnumeration {
- Enumeration list;
-
- ValuesEnumImpl() {
- list = values.elements();
- }
-
- public boolean hasMoreElements() {
- return list.hasMoreElements();
- }
-
- public Object nextElement() {
- return(list.nextElement());
- }
-
- public Object next() throws NamingException {
- return list.nextElement();
- }
-
- public boolean hasMore() throws NamingException {
- return list.hasMoreElements();
- }
-
- public void close() throws NamingException {
- list = null;
- }
- }
-
- /**
- * Use serialVersionUID from JNDI 1.1.1 for interoperability.
- */
- private static final long serialVersionUID = 6743528196119291326L;
- }