You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
558 lines
18 KiB
558 lines
18 KiB
/*
|
|
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. 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
|
|
* @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<Object> values;
|
|
|
|
/**
|
|
* A flag for recording whether this attribute's values are ordered.
|
|
* @serial
|
|
*/
|
|
protected boolean ordered = false;
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public Object clone() {
|
|
BasicAttribute attr;
|
|
try {
|
|
attr = (BasicAttribute)super.clone();
|
|
} catch (CloneNotSupportedException e) {
|
|
attr = new BasicAttribute(attrID, ordered);
|
|
}
|
|
attr.values = (Vector<Object>)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<Object> 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
|
|
|
|
/**
|
|
* Overridden 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));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Overridden 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<>(Math.min(1024, n));
|
|
while (--n >= 0) {
|
|
values.addElement(s.readObject());
|
|
}
|
|
}
|
|
|
|
|
|
class ValuesEnumImpl implements NamingEnumeration<Object> {
|
|
Enumeration<Object> 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;
|
|
}
|