Path: blob/master/src/java.naming/share/classes/javax/naming/directory/BasicAttribute.java
41159 views
/*1* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.naming.directory;2627import java.util.Vector;28import java.util.Enumeration;29import java.util.NoSuchElementException;30import java.lang.reflect.Array;3132import javax.naming.NamingException;33import javax.naming.NamingEnumeration;34import javax.naming.OperationNotSupportedException;3536/**37* This class provides a basic implementation of the {@code Attribute} interface.38*<p>39* This implementation does not support the schema methods40* {@code getAttributeDefinition()} and {@code getAttributeSyntaxDefinition()}.41* They simply throw {@code OperationNotSupportedException}.42* Subclasses of {@code BasicAttribute} should override these methods if they43* support them.44*<p>45* The {@code BasicAttribute} class by default uses {@code Object.equals()} to46* determine equality of attribute values when testing for equality or47* when searching for values, <em>except</em> when the value is an array.48* For an array, each element of the array is checked using {@code Object.equals()}.49* Subclasses of {@code BasicAttribute} can make use of schema information50* when doing similar equality checks by overriding methods51* in which such use of schema is meaningful.52* Similarly, the {@code BasicAttribute} class by default returns the values passed to its53* constructor and/or manipulated using the add/remove methods.54* Subclasses of {@code BasicAttribute} can override {@code get()} and {@code getAll()}55* to get the values dynamically from the directory (or implement56* the {@code Attribute} interface directly instead of subclassing {@code BasicAttribute}).57*<p>58* Note that updates to {@code BasicAttribute} (such as adding or removing a value)59* does not affect the corresponding representation of the attribute60* in the directory. Updates to the directory can only be effected61* using operations in the {@code DirContext} interface.62*<p>63* A {@code BasicAttribute} instance is not synchronized against concurrent64* multithreaded access. Multiple threads trying to access and modify a65* {@code BasicAttribute} should lock the object.66*67* @author Rosanna Lee68* @author Scott Seligman69* @since 1.370*/71public class BasicAttribute implements Attribute {72/**73* Holds the attribute's id. It is initialized by the public constructor and74* cannot be null unless methods in BasicAttribute that use attrID75* have been overridden.76* @serial77*/78protected String attrID;7980/**81* Holds the attribute's values. Initialized by public constructors.82* Cannot be null unless methods in BasicAttribute that use83* values have been overridden.84*/85protected transient Vector<Object> values;8687/**88* A flag for recording whether this attribute's values are ordered.89* @serial90*/91protected boolean ordered = false;9293@SuppressWarnings("unchecked")94public Object clone() {95BasicAttribute attr;96try {97attr = (BasicAttribute)super.clone();98} catch (CloneNotSupportedException e) {99attr = new BasicAttribute(attrID, ordered);100}101attr.values = (Vector<Object>)values.clone();102return attr;103}104105/**106* Determines whether obj is equal to this attribute.107* Two attributes are equal if their attribute-ids, syntaxes108* and values are equal.109* If the attribute values are unordered, the order that the values were added110* are irrelevant. If the attribute values are ordered, then the111* order the values must match.112* If obj is null or not an Attribute, false is returned.113*<p>114* By default {@code Object.equals()} is used when comparing the attribute115* id and its values except when a value is an array. For an array,116* each element of the array is checked using {@code Object.equals()}.117* A subclass may override this to make118* use of schema syntax information and matching rules,119* which define what it means for two attributes to be equal.120* How and whether a subclass makes121* use of the schema information is determined by the subclass.122* If a subclass overrides {@code equals()}, it should also override123* {@code hashCode()}124* such that two attributes that are equal have the same hash code.125*126* @param obj The possibly null object to check.127* @return true if obj is equal to this attribute; false otherwise.128* @see #hashCode129* @see #contains130*/131public boolean equals(Object obj) {132if ((obj != null) && (obj instanceof Attribute)) {133Attribute target = (Attribute)obj;134135// Check order first136if (isOrdered() != target.isOrdered()) {137return false;138}139int len;140if (attrID.equals(target.getID()) &&141(len=size()) == target.size()) {142try {143if (isOrdered()) {144// Go through both list of values145for (int i = 0; i < len; i++) {146if (!valueEquals(get(i), target.get(i))) {147return false;148}149}150} else {151// order is not relevant; check for existence152Enumeration<?> theirs = target.getAll();153while (theirs.hasMoreElements()) {154if (find(theirs.nextElement()) < 0)155return false;156}157}158} catch (NamingException e) {159return false;160}161return true;162}163}164return false;165}166167/**168* Calculates the hash code of this attribute.169*<p>170* The hash code is computed by adding the hash code of171* the attribute's id and that of all of its values except for172* values that are arrays.173* For an array, the hash code of each element of the array is summed.174* If a subclass overrides {@code hashCode()}, it should override175* {@code equals()}176* as well so that two attributes that are equal have the same hash code.177*178* @return an int representing the hash code of this attribute.179* @see #equals180*/181public int hashCode() {182int hash = attrID.hashCode();183int num = values.size();184Object val;185for (int i = 0; i < num; i ++) {186val = values.elementAt(i);187if (val != null) {188if (val.getClass().isArray()) {189Object it;190int len = Array.getLength(val);191for (int j = 0 ; j < len ; j++) {192it = Array.get(val, j);193if (it != null) {194hash += it.hashCode();195}196}197} else {198hash += val.hashCode();199}200}201}202return hash;203}204205/**206* Generates the string representation of this attribute.207* The string consists of the attribute's id and its values.208* This string is meant for debugging and not meant to be209* interpreted programmatically.210* @return The non-null string representation of this attribute.211*/212public String toString() {213StringBuilder answer = new StringBuilder(attrID + ": ");214if (values.size() == 0) {215answer.append("No values");216} else {217boolean start = true;218for (Enumeration<Object> e = values.elements(); e.hasMoreElements(); ) {219if (!start)220answer.append(", ");221answer.append(e.nextElement());222start = false;223}224}225return answer.toString();226}227228/**229* Constructs a new instance of an unordered attribute with no value.230*231* @param id The attribute's id. It cannot be null.232*/233public BasicAttribute(String id) {234this(id, false);235}236237/**238* Constructs a new instance of an unordered attribute with a single value.239*240* @param id The attribute's id. It cannot be null.241* @param value The attribute's value. If null, a null242* value is added to the attribute.243*/244public BasicAttribute(String id, Object value) {245this(id, value, false);246}247248/**249* Constructs a new instance of a possibly ordered attribute with no value.250*251* @param id The attribute's id. It cannot be null.252* @param ordered true means the attribute's values will be ordered;253* false otherwise.254*/255public BasicAttribute(String id, boolean ordered) {256attrID = id;257values = new Vector<>();258this.ordered = ordered;259}260261/**262* Constructs a new instance of a possibly ordered attribute with a263* single value.264*265* @param id The attribute's id. It cannot be null.266* @param value The attribute's value. If null, a null267* value is added to the attribute.268* @param ordered true means the attribute's values will be ordered;269* false otherwise.270*/271public BasicAttribute(String id, Object value, boolean ordered) {272this(id, ordered);273values.addElement(value);274}275276/**277* Retrieves an enumeration of this attribute's values.278*<p>279* By default, the values returned are those passed to the280* constructor and/or manipulated using the add/replace/remove methods.281* A subclass may override this to retrieve the values dynamically282* from the directory.283*/284public NamingEnumeration<?> getAll() throws NamingException {285return new ValuesEnumImpl();286}287288/**289* Retrieves one of this attribute's values.290*<p>291* By default, the value returned is one of those passed to the292* constructor and/or manipulated using the add/replace/remove methods.293* A subclass may override this to retrieve the value dynamically294* from the directory.295*/296public Object get() throws NamingException {297if (values.size() == 0) {298throw new299NoSuchElementException("Attribute " + getID() + " has no value");300} else {301return values.elementAt(0);302}303}304305public int size() {306return values.size();307}308309public String getID() {310return attrID;311}312313/**314* Determines whether a value is in this attribute.315*<p>316* By default,317* {@code Object.equals()} is used when comparing {@code attrVal}318* with this attribute's values except when {@code attrVal} is an array.319* For an array, each element of the array is checked using320* {@code Object.equals()}.321* A subclass may use schema information to determine equality.322*/323public boolean contains(Object attrVal) {324return (find(attrVal) >= 0);325}326327// For finding first element that has a null in JDK1.1 Vector.328// In the Java 2 platform, can just replace this with Vector.indexOf(target);329private int find(Object target) {330Class<?> cl;331if (target == null) {332int ct = values.size();333for (int i = 0 ; i < ct ; i++) {334if (values.elementAt(i) == null)335return i;336}337} else if ((cl=target.getClass()).isArray()) {338int ct = values.size();339Object it;340for (int i = 0 ; i < ct ; i++) {341it = values.elementAt(i);342if (it != null && cl == it.getClass()343&& arrayEquals(target, it))344return i;345}346} else {347return values.indexOf(target, 0);348}349return -1; // not found350}351352/**353* Determines whether two attribute values are equal.354* Use arrayEquals for arrays and {@code Object.equals()} otherwise.355*/356private static boolean valueEquals(Object obj1, Object obj2) {357if (obj1 == obj2) {358return true; // object references are equal359}360if (obj1 == null) {361return false; // obj2 was not false362}363if (obj1.getClass().isArray() &&364obj2.getClass().isArray()) {365return arrayEquals(obj1, obj2);366}367return (obj1.equals(obj2));368}369370/**371* Determines whether two arrays are equal by comparing each of their372* elements using {@code Object.equals()}.373*/374private static boolean arrayEquals(Object a1, Object a2) {375int len;376if ((len = Array.getLength(a1)) != Array.getLength(a2))377return false;378379for (int j = 0; j < len; j++) {380Object i1 = Array.get(a1, j);381Object i2 = Array.get(a2, j);382if (i1 == null || i2 == null) {383if (i1 != i2)384return false;385} else if (!i1.equals(i2)) {386return false;387}388}389return true;390}391392/**393* Adds a new value to this attribute.394*<p>395* By default, {@code Object.equals()} is used when comparing {@code attrVal}396* with this attribute's values except when {@code attrVal} is an array.397* For an array, each element of the array is checked using398* {@code Object.equals()}.399* A subclass may use schema information to determine equality.400*/401public boolean add(Object attrVal) {402if (isOrdered() || (find(attrVal) < 0)) {403values.addElement(attrVal);404return true;405} else {406return false;407}408}409410/**411* Removes a specified value from this attribute.412*<p>413* By default, {@code Object.equals()} is used when comparing {@code attrVal}414* with this attribute's values except when {@code attrVal} is an array.415* For an array, each element of the array is checked using416* {@code Object.equals()}.417* A subclass may use schema information to determine equality.418*/419public boolean remove(Object attrval) {420// For the Java 2 platform, can just use "return removeElement(attrval);"421// Need to do the following to handle null case422423int i = find(attrval);424if (i >= 0) {425values.removeElementAt(i);426return true;427}428return false;429}430431public void clear() {432values.setSize(0);433}434435// ---- ordering methods436437public boolean isOrdered() {438return ordered;439}440441public Object get(int ix) throws NamingException {442return values.elementAt(ix);443}444445public Object remove(int ix) {446Object answer = values.elementAt(ix);447values.removeElementAt(ix);448return answer;449}450451public void add(int ix, Object attrVal) {452if (!isOrdered() && contains(attrVal)) {453throw new IllegalStateException(454"Cannot add duplicate to unordered attribute");455}456values.insertElementAt(attrVal, ix);457}458459public Object set(int ix, Object attrVal) {460if (!isOrdered() && contains(attrVal)) {461throw new IllegalStateException(462"Cannot add duplicate to unordered attribute");463}464465Object answer = values.elementAt(ix);466values.setElementAt(attrVal, ix);467return answer;468}469470// ----------------- Schema methods471472/**473* Retrieves the syntax definition associated with this attribute.474*<p>475* This method by default throws OperationNotSupportedException. A subclass476* should override this method if it supports schema.477*/478public DirContext getAttributeSyntaxDefinition() throws NamingException {479throw new OperationNotSupportedException("attribute syntax");480}481482/**483* Retrieves this attribute's schema definition.484*<p>485* This method by default throws OperationNotSupportedException. A subclass486* should override this method if it supports schema.487*/488public DirContext getAttributeDefinition() throws NamingException {489throw new OperationNotSupportedException("attribute definition");490}491492493// ---- serialization methods494495/**496* The writeObject method is called to save the state of the497* {@code BasicAttribute} to a stream.498*499* @serialData Default field (the attribute ID - a {@code String}),500* followed by the number of values (an {@code int}), and the501* individual values.502*503* @param s the {@code ObjectOutputStream} to write to504* @throws java.io.IOException if an I/O error occurs505*/506@java.io.Serial507private void writeObject(java.io.ObjectOutputStream s)508throws java.io.IOException {509// Overridden to avoid exposing implementation details510s.defaultWriteObject(); // write out the attrID511s.writeInt(values.size());512for (int i = 0; i < values.size(); i++) {513s.writeObject(values.elementAt(i));514}515}516517/**518* The readObject method is called to restore the state of519* the {@code BasicAttribute} from a stream.520*521* See {@code writeObject} for a description of the serial form.522*523* @param s the {@code ObjectInputStream} to read from524* @throws java.io.IOException if an I/O error occurs525* @throws ClassNotFoundException if the class of a serialized object526* could not be found527*/528@java.io.Serial529private void readObject(java.io.ObjectInputStream s)530throws java.io.IOException, ClassNotFoundException {531// Overridden to avoid exposing implementation details.532s.defaultReadObject(); // read in the attrID533int n = s.readInt(); // number of values534values = new Vector<>(Math.min(1024, n));535while (--n >= 0) {536values.addElement(s.readObject());537}538}539540541class ValuesEnumImpl implements NamingEnumeration<Object> {542Enumeration<Object> list;543544ValuesEnumImpl() {545list = values.elements();546}547548public boolean hasMoreElements() {549return list.hasMoreElements();550}551552public Object nextElement() {553return(list.nextElement());554}555556public Object next() throws NamingException {557return list.nextElement();558}559560public boolean hasMore() throws NamingException {561return list.hasMoreElements();562}563564public void close() throws NamingException {565list = null;566}567}568569/**570* Use serialVersionUID from JNDI 1.1.1 for interoperability.571*/572@java.io.Serial573private static final long serialVersionUID = 6743528196119291326L;574}575576577