Path: blob/master/src/java.naming/share/classes/javax/naming/directory/BasicAttributes.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*/242526package javax.naming.directory;2728import java.util.Hashtable;29import java.util.Enumeration;30import java.util.Locale;3132import javax.naming.NamingException;33import javax.naming.NamingEnumeration;3435/**36* This class provides a basic implementation37* of the Attributes interface.38*<p>39* BasicAttributes is either case-sensitive or case-insensitive (case-ignore).40* This property is determined at the time the BasicAttributes constructor41* is called.42* In a case-insensitive BasicAttributes, the case of its attribute identifiers43* is ignored when searching for an attribute, or adding attributes.44* In a case-sensitive BasicAttributes, the case is significant.45*<p>46* When the BasicAttributes class needs to create an Attribute, it47* uses BasicAttribute. There is no other dependency on BasicAttribute.48*<p>49* Note that updates to BasicAttributes (such as adding or removing an attribute)50* does not affect the corresponding representation in the directory.51* Updates to the directory can only be effected52* using operations in the DirContext interface.53*<p>54* A BasicAttributes instance is not synchronized against concurrent55* multithreaded access. Multiple threads trying to access and modify56* a single BasicAttributes instance should lock the object.57*58* @author Rosanna Lee59* @author Scott Seligman60*61* @see DirContext#getAttributes62* @see DirContext#modifyAttributes63* @see DirContext#bind64* @see DirContext#rebind65* @see DirContext#createSubcontext66* @see DirContext#search67* @since 1.368*/6970public class BasicAttributes implements Attributes {71/**72* Indicates whether case of attribute ids is ignored.73* @serial74*/75private boolean ignoreCase = false;7677// The 'key' in attrs is stored in the 'right case'.78// If ignoreCase is true, key is aways lowercase.79// If ignoreCase is false, key is stored as supplied by put().80// %%% Not declared "private" due to bug 4064984.81transient Hashtable<String,Attribute> attrs = new Hashtable<>(11);8283/**84* Constructs a new instance of Attributes.85* The character case of attribute identifiers86* is significant when subsequently retrieving or adding attributes.87*/88public BasicAttributes() {89}9091/**92* Constructs a new instance of Attributes.93* If <code>ignoreCase</code> is true, the character case of attribute94* identifiers is ignored; otherwise the case is significant.95* @param ignoreCase true means this attribute set will ignore96* the case of its attribute identifiers97* when retrieving or adding attributes;98* false means case is respected.99*/100public BasicAttributes(boolean ignoreCase) {101this.ignoreCase = ignoreCase;102}103104/**105* Constructs a new instance of Attributes with one attribute.106* The attribute specified by attrID and val are added to the newly107* created attribute.108* The character case of attribute identifiers109* is significant when subsequently retrieving or adding attributes.110* @param attrID non-null The id of the attribute to add.111* @param val The value of the attribute to add. If null, a null112* value is added to the attribute.113*/114public BasicAttributes(String attrID, Object val) {115this();116this.put(new BasicAttribute(attrID, val));117}118119/**120* Constructs a new instance of Attributes with one attribute.121* The attribute specified by attrID and val are added to the newly122* created attribute.123* If <code>ignoreCase</code> is true, the character case of attribute124* identifiers is ignored; otherwise the case is significant.125* @param attrID non-null The id of the attribute to add.126* If this attribute set ignores the character127* case of its attribute ids, the case of attrID128* is ignored.129* @param val The value of the attribute to add. If null, a null130* value is added to the attribute.131* @param ignoreCase true means this attribute set will ignore132* the case of its attribute identifiers133* when retrieving or adding attributes;134* false means case is respected.135*/136public BasicAttributes(String attrID, Object val, boolean ignoreCase) {137this(ignoreCase);138this.put(new BasicAttribute(attrID, val));139}140141@SuppressWarnings("unchecked")142public Object clone() {143BasicAttributes attrset;144try {145attrset = (BasicAttributes)super.clone();146} catch (CloneNotSupportedException e) {147attrset = new BasicAttributes(ignoreCase);148}149attrset.attrs = (Hashtable<String,Attribute>)attrs.clone();150return attrset;151}152153public boolean isCaseIgnored() {154return ignoreCase;155}156157public int size() {158return attrs.size();159}160161public Attribute get(String attrID) {162Attribute attr = attrs.get(163ignoreCase ? attrID.toLowerCase(Locale.ENGLISH) : attrID);164return (attr);165}166167public NamingEnumeration<Attribute> getAll() {168return new AttrEnumImpl();169}170171public NamingEnumeration<String> getIDs() {172return new IDEnumImpl();173}174175public Attribute put(String attrID, Object val) {176return this.put(new BasicAttribute(attrID, val));177}178179public Attribute put(Attribute attr) {180String id = attr.getID();181if (ignoreCase) {182id = id.toLowerCase(Locale.ENGLISH);183}184return attrs.put(id, attr);185}186187public Attribute remove(String attrID) {188String id = (ignoreCase ? attrID.toLowerCase(Locale.ENGLISH) : attrID);189return attrs.remove(id);190}191192/**193* Generates the string representation of this attribute set.194* The string consists of each attribute identifier and the contents195* of each attribute. The contents of this string is useful196* for debugging and is not meant to be interpreted programmatically.197*198* @return A non-null string listing the contents of this attribute set.199*/200public String toString() {201if (attrs.size() == 0) {202return("No attributes");203} else {204return attrs.toString();205}206}207208/**209* Determines whether this {@code BasicAttributes} is equal to another210* {@code Attributes}211* Two {@code Attributes} are equal if they are both instances of212* {@code Attributes},213* treat the case of attribute IDs the same way, and contain the214* same attributes. Each {@code Attribute} in this {@code BasicAttributes}215* is checked for equality using {@code Object.equals()}, which may have216* be overridden by implementations of {@code Attribute}).217* If a subclass overrides {@code equals()},218* it should override {@code hashCode()}219* as well so that two {@code Attributes} instances that are equal220* have the same hash code.221* @param obj the possibly null object to compare against.222*223* @return true If obj is equal to this BasicAttributes.224* @see #hashCode225*/226public boolean equals(Object obj) {227if ((obj != null) && (obj instanceof Attributes)) {228Attributes target = (Attributes)obj;229230// Check case first231if (ignoreCase != target.isCaseIgnored()) {232return false;233}234235if (size() == target.size()) {236Attribute their, mine;237try {238NamingEnumeration<?> theirs = target.getAll();239while (theirs.hasMore()) {240their = (Attribute)theirs.next();241mine = get(their.getID());242if (!their.equals(mine)) {243return false;244}245}246} catch (NamingException e) {247return false;248}249return true;250}251}252return false;253}254255/**256* Calculates the hash code of this BasicAttributes.257*<p>258* The hash code is computed by adding the hash code of259* the attributes of this object. If this BasicAttributes260* ignores case of its attribute IDs, one is added to the hash code.261* If a subclass overrides {@code hashCode()},262* it should override {@code equals()}263* as well so that two {@code Attributes} instances that are equal264* have the same hash code.265*266* @return an int representing the hash code of this BasicAttributes instance.267* @see #equals268*/269public int hashCode() {270int hash = (ignoreCase ? 1 : 0);271try {272NamingEnumeration<?> all = getAll();273while (all.hasMore()) {274hash += all.next().hashCode();275}276} catch (NamingException e) {}277return hash;278}279280/**281* The writeObject method is called to save the state of the282* {@code BasicAttributes} to a stream.283*284* @serialData Default field (ignoreCase flag - a {@code boolean}), followed by285* the number of attributes in the set286* (an {@code int}), and then the individual {@code Attribute} objects.287*288* @param s the {@code ObjectOutputStream} to write to289* @throws java.io.IOException if an I/O error occurs290*/291@java.io.Serial292private void writeObject(java.io.ObjectOutputStream s)293throws java.io.IOException {294// Overridden to avoid exposing implementation details295s.defaultWriteObject(); // write out the ignoreCase flag296s.writeInt(attrs.size());297Enumeration<Attribute> attrEnum = attrs.elements();298while (attrEnum.hasMoreElements()) {299s.writeObject(attrEnum.nextElement());300}301}302303/**304* The readObject method is called to restore the state of305* the {@code BasicAttributes} from a stream.306*307* See {@code writeObject} for a description of the serial form.308*309* @param s the {@code ObjectInputStream} to read from310* @throws java.io.IOException if an I/O error occurs311* @throws ClassNotFoundException if the class of a serialized object312* could not be found313*/314@java.io.Serial315private void readObject(java.io.ObjectInputStream s)316throws java.io.IOException, ClassNotFoundException {317// Overridden to avoid exposing implementation details.318s.defaultReadObject(); // read in the ignoreCase flag319int n = s.readInt(); // number of attributes320attrs = (n >= 1)321? new Hashtable<>(1 + (int) (Math.min(768, n) / .75f))322: new Hashtable<>(2); // can't have initial size of 0 (grrr...)323while (--n >= 0) {324put((Attribute)s.readObject());325}326}327328329class AttrEnumImpl implements NamingEnumeration<Attribute> {330331Enumeration<Attribute> elements;332333public AttrEnumImpl() {334this.elements = attrs.elements();335}336337public boolean hasMoreElements() {338return elements.hasMoreElements();339}340341public Attribute nextElement() {342return elements.nextElement();343}344345public boolean hasMore() throws NamingException {346return hasMoreElements();347}348349public Attribute next() throws NamingException {350return nextElement();351}352353public void close() throws NamingException {354elements = null;355}356}357358class IDEnumImpl implements NamingEnumeration<String> {359360Enumeration<Attribute> elements;361362public IDEnumImpl() {363// Walking through the elements, rather than the keys, gives364// us attribute IDs that have not been converted to lowercase.365this.elements = attrs.elements();366}367368public boolean hasMoreElements() {369return elements.hasMoreElements();370}371372public String nextElement() {373Attribute attr = elements.nextElement();374return attr.getID();375}376377public boolean hasMore() throws NamingException {378return hasMoreElements();379}380381public String next() throws NamingException {382return nextElement();383}384385public void close() throws NamingException {386elements = null;387}388}389390/**391* Use serialVersionUID from JNDI 1.1.1 for interoperability.392*/393@java.io.Serial394private static final long serialVersionUID = 4980164073184639448L;395}396397398