/* | 
|
 * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved. | 
|
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
|
 * | 
|
 * This code is free software; you can redistribute it and/or modify it | 
|
 * under the terms of the GNU General Public License version 2 only, as | 
|
 * published by the Free Software Foundation.  Oracle designates this | 
|
 * particular file as subject to the "Classpath" exception as provided | 
|
 * by Oracle in the LICENSE file that accompanied this code. | 
|
 * | 
|
 * This code is distributed in the hope that it will be useful, but WITHOUT | 
|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
|
 * version 2 for more details (a copy is included in the LICENSE file that | 
|
 * accompanied this code). | 
|
 * | 
|
 * You should have received a copy of the GNU General Public License version | 
|
 * 2 along with this work; if not, write to the Free Software Foundation, | 
|
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
|
 * | 
|
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
|
 * or visit www.oracle.com if you need additional information or have any | 
|
 * questions. | 
|
*/  | 
|
package javax.naming.directory;  | 
|
import java.util.Hashtable;  | 
|
import java.util.Enumeration;  | 
|
import java.util.Locale;  | 
|
import javax.naming.NamingException;  | 
|
import javax.naming.NamingEnumeration;  | 
|
/**  | 
|
* This class provides a basic implementation  | 
|
* of the Attributes interface.  | 
|
*<p>  | 
|
* BasicAttributes is either case-sensitive or case-insensitive (case-ignore).  | 
|
* This property is determined at the time the BasicAttributes constructor  | 
|
* is called.  | 
|
* In a case-insensitive BasicAttributes, the case of its attribute identifiers  | 
|
* is ignored when searching for an attribute, or adding attributes.  | 
|
* In a case-sensitive BasicAttributes, the case is significant.  | 
|
*<p>  | 
|
* When the BasicAttributes class needs to create an Attribute, it  | 
|
* uses BasicAttribute. There is no other dependency on BasicAttribute.  | 
|
*<p>  | 
|
* Note that updates to BasicAttributes (such as adding or removing an attribute)  | 
|
* does not affect the corresponding representation in the directory.  | 
|
* Updates to the directory can only be effected  | 
|
* using operations in the DirContext interface.  | 
|
*<p>  | 
|
* A BasicAttributes instance is not synchronized against concurrent  | 
|
* multithreaded access. Multiple threads trying to access and modify  | 
|
* a single BasicAttributes instance should lock the object.  | 
|
*  | 
|
* @author Rosanna Lee  | 
|
* @author Scott Seligman  | 
|
*  | 
|
* @see DirContext#getAttributes  | 
|
* @see DirContext#modifyAttributes  | 
|
* @see DirContext#bind  | 
|
* @see DirContext#rebind  | 
|
* @see DirContext#createSubcontext  | 
|
* @see DirContext#search  | 
|
* @since 1.3  | 
|
*/  | 
|
public class BasicAttributes implements Attributes {  | 
|
    /** | 
|
     * Indicates whether case of attribute ids is ignored. | 
|
     * @serial | 
|
*/  | 
|
private boolean ignoreCase = false;  | 
|
// The 'key' in attrs is stored in the 'right case'.  | 
|
// If ignoreCase is true, key is aways lowercase.  | 
|
// If ignoreCase is false, key is stored as supplied by put().  | 
|
    // %%% Not declared "private" due to bug 4064984. | 
|
transient Hashtable<String,Attribute> attrs = new Hashtable<>(11);  | 
|
    /** | 
|
      * Constructs a new instance of Attributes. | 
|
      * The character case of attribute identifiers | 
|
      * is significant when subsequently retrieving or adding attributes. | 
|
*/  | 
|
    public BasicAttributes() { | 
|
}  | 
|
    /** | 
|
      * Constructs a new instance of Attributes. | 
|
      * If <code>ignoreCase</code> is true, the character case of attribute | 
|
      * identifiers is ignored; otherwise the case is significant. | 
|
      * @param ignoreCase true means this attribute set will ignore | 
|
      *                   the case of its attribute identifiers | 
|
      *                   when retrieving or adding attributes; | 
|
      *                   false means case is respected. | 
|
*/  | 
|
    public BasicAttributes(boolean ignoreCase) { | 
|
this.ignoreCase = ignoreCase;  | 
|
}  | 
|
    /** | 
|
      * Constructs a new instance of Attributes with one attribute. | 
|
      * The attribute specified by attrID and val are added to the newly | 
|
      * created attribute. | 
|
      * The character case of attribute identifiers | 
|
      * is significant when subsequently retrieving or adding attributes. | 
|
      * @param attrID   non-null The id of the attribute to add. | 
|
      * @param val The value of the attribute to add. If null, a null | 
|
      *        value is added to the attribute. | 
|
*/  | 
|
public BasicAttributes(String attrID, Object val) {  | 
|
this();  | 
|
this.put(new BasicAttribute(attrID, val));  | 
|
}  | 
|
    /** | 
|
      * Constructs a new instance of Attributes with one attribute. | 
|
      * The attribute specified by attrID and val are added to the newly | 
|
      * created attribute. | 
|
      * If <code>ignoreCase</code> is true, the character case of attribute | 
|
      * identifiers is ignored; otherwise the case is significant. | 
|
      * @param attrID   non-null The id of the attribute to add. | 
|
      *           If this attribute set ignores the character | 
|
      *           case of its attribute ids, the case of attrID | 
|
      *           is ignored. | 
|
      * @param val The value of the attribute to add. If null, a null | 
|
      *        value is added to the attribute. | 
|
      * @param ignoreCase true means this attribute set will ignore | 
|
      *                   the case of its attribute identifiers | 
|
      *                   when retrieving or adding attributes; | 
|
      *                   false means case is respected. | 
|
*/  | 
|
public BasicAttributes(String attrID, Object val, boolean ignoreCase) {  | 
|
this(ignoreCase);  | 
|
this.put(new BasicAttribute(attrID, val));  | 
|
}  | 
|
    @SuppressWarnings("unchecked") | 
|
public Object clone() {  | 
|
BasicAttributes attrset;  | 
|
        try { | 
|
attrset = (BasicAttributes)super.clone();  | 
|
} catch (CloneNotSupportedException e) {  | 
|
attrset = new BasicAttributes(ignoreCase);  | 
|
}  | 
|
attrset.attrs = (Hashtable<String,Attribute>)attrs.clone();  | 
|
return attrset;  | 
|
}  | 
|
    public boolean isCaseIgnored() { | 
|
return ignoreCase;  | 
|
}  | 
|
    public int size() { | 
|
return attrs.size();  | 
|
}  | 
|
public Attribute get(String attrID) {  | 
|
Attribute attr = attrs.get(  | 
|
ignoreCase ? attrID.toLowerCase(Locale.ENGLISH) : attrID);  | 
|
return (attr);  | 
|
}  | 
|
public NamingEnumeration<Attribute> getAll() {  | 
|
return new AttrEnumImpl();  | 
|
}  | 
|
public NamingEnumeration<String> getIDs() {  | 
|
return new IDEnumImpl();  | 
|
}  | 
|
public Attribute put(String attrID, Object val) {  | 
|
return this.put(new BasicAttribute(attrID, val));  | 
|
}  | 
|
public Attribute put(Attribute attr) {  | 
|
String id = attr.getID();  | 
|
if (ignoreCase) {  | 
|
id = id.toLowerCase(Locale.ENGLISH);  | 
|
}  | 
|
return attrs.put(id, attr);  | 
|
}  | 
|
public Attribute remove(String attrID) {  | 
|
String id = (ignoreCase ? attrID.toLowerCase(Locale.ENGLISH) : attrID);  | 
|
return attrs.remove(id);  | 
|
}  | 
|
    /** | 
|
     * Generates the string representation of this attribute set. | 
|
     * The string consists of each attribute identifier and the contents | 
|
     * of each attribute. The contents of this string is useful | 
|
     * for debugging and is not meant to be interpreted programmatically. | 
|
     * | 
|
     * @return A non-null string listing the contents of this attribute set. | 
|
*/  | 
|
public String toString() {  | 
|
if (attrs.size() == 0) {  | 
|
            return("No attributes"); | 
|
        } else { | 
|
return attrs.toString();  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Determines whether this <tt>BasicAttributes</tt> is equal to another | 
|
     * <tt>Attributes</tt> | 
|
     * Two <tt>Attributes</tt> are equal if they are both instances of | 
|
     * <tt>Attributes</tt>, | 
|
     * treat the case of attribute IDs the same way, and contain the | 
|
     * same attributes. Each <tt>Attribute</tt> in this <tt>BasicAttributes</tt> | 
|
     * is checked for equality using <tt>Object.equals()</tt>, which may have | 
|
     * be overridden by implementations of <tt>Attribute</tt>). | 
|
     * If a subclass overrides <tt>equals()</tt>, | 
|
     * it should override <tt>hashCode()</tt> | 
|
     * as well so that two <tt>Attributes</tt> instances that are equal | 
|
     * have the same hash code. | 
|
     * @param obj the possibly null object to compare against. | 
|
     * | 
|
     * @return true If obj is equal to this BasicAttributes. | 
|
     * @see #hashCode | 
|
*/  | 
|
public boolean equals(Object obj) {  | 
|
if ((obj != null) && (obj instanceof Attributes)) {  | 
|
Attributes target = (Attributes)obj;  | 
|
            // Check case first | 
|
if (ignoreCase != target.isCaseIgnored()) {  | 
|
return false;  | 
|
}  | 
|
if (size() == target.size()) {  | 
|
Attribute their, mine;  | 
|
                try { | 
|
NamingEnumeration<?> theirs = target.getAll();  | 
|
while (theirs.hasMore()) {  | 
|
their = (Attribute)theirs.next();  | 
|
mine = get(their.getID());  | 
|
if (!their.equals(mine)) {  | 
|
return false;  | 
|
}  | 
|
}  | 
|
} catch (NamingException e) {  | 
|
return false;  | 
|
}  | 
|
return true;  | 
|
}  | 
|
}  | 
|
return false;  | 
|
}  | 
|
    /** | 
|
     * Calculates the hash code of this BasicAttributes. | 
|
     *<p> | 
|
     * The hash code is computed by adding the hash code of | 
|
     * the attributes of this object. If this BasicAttributes | 
|
     * ignores case of its attribute IDs, one is added to the hash code. | 
|
     * If a subclass overrides <tt>hashCode()</tt>, | 
|
     * it should override <tt>equals()</tt> | 
|
     * as well so that two <tt>Attributes</tt> instances that are equal | 
|
     * have the same hash code. | 
|
     * | 
|
     * @return an int representing the hash code of this BasicAttributes instance. | 
|
     * @see #equals | 
|
*/  | 
|
    public int hashCode() { | 
|
int hash = (ignoreCase ? 1 : 0);  | 
|
        try { | 
|
NamingEnumeration<?> all = getAll();  | 
|
while (all.hasMore()) {  | 
|
hash += all.next().hashCode();  | 
|
}  | 
|
} catch (NamingException e) {}  | 
|
return hash;  | 
|
}  | 
|
    /** | 
|
     * Overridden to avoid exposing implementation details. | 
|
     * @serialData Default field (ignoreCase flag -- a boolean), followed by | 
|
     * the number of attributes in the set | 
|
     * (an int), and then the individual Attribute objects. | 
|
*/  | 
|
private void writeObject(java.io.ObjectOutputStream s)  | 
|
            throws java.io.IOException { | 
|
s.defaultWriteObject(); // write out the ignoreCase flag  | 
|
s.writeInt(attrs.size());  | 
|
Enumeration<Attribute> attrEnum = attrs.elements();  | 
|
while (attrEnum.hasMoreElements()) {  | 
|
s.writeObject(attrEnum.nextElement());  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Overridden to avoid exposing implementation details. | 
|
*/  | 
|
private void readObject(java.io.ObjectInputStream s)  | 
|
throws java.io.IOException, ClassNotFoundException {  | 
|
s.defaultReadObject(); // read in the ignoreCase flag  | 
|
int n = s.readInt(); // number of attributes  | 
|
attrs = (n >= 1)  | 
|
? new Hashtable<>(1 + (int) (Math.min(768, n) / .75f))  | 
|
: new Hashtable<>(2); // can't have initial size of 0 (grrr...)  | 
|
while (--n >= 0) {  | 
|
put((Attribute)s.readObject());  | 
|
}  | 
|
}  | 
|
class AttrEnumImpl implements NamingEnumeration<Attribute> {  | 
|
Enumeration<Attribute> elements;  | 
|
    public AttrEnumImpl() { | 
|
this.elements = attrs.elements();  | 
|
}  | 
|
    public boolean hasMoreElements() { | 
|
return elements.hasMoreElements();  | 
|
}  | 
|
public Attribute nextElement() {  | 
|
return elements.nextElement();  | 
|
}  | 
|
public boolean hasMore() throws NamingException {  | 
|
return hasMoreElements();  | 
|
}  | 
|
public Attribute next() throws NamingException {  | 
|
return nextElement();  | 
|
}  | 
|
public void close() throws NamingException {  | 
|
elements = null;  | 
|
}  | 
|
}  | 
|
class IDEnumImpl implements NamingEnumeration<String> {  | 
|
Enumeration<Attribute> elements;  | 
|
    public IDEnumImpl() { | 
|
// Walking through the elements, rather than the keys, gives  | 
|
        // us attribute IDs that have not been converted to lowercase. | 
|
this.elements = attrs.elements();  | 
|
}  | 
|
    public boolean hasMoreElements() { | 
|
return elements.hasMoreElements();  | 
|
}  | 
|
public String nextElement() {  | 
|
Attribute attr = elements.nextElement();  | 
|
return attr.getID();  | 
|
}  | 
|
public boolean hasMore() throws NamingException {  | 
|
return hasMoreElements();  | 
|
}  | 
|
public String next() throws NamingException {  | 
|
return nextElement();  | 
|
}  | 
|
public void close() throws NamingException {  | 
|
elements = null;  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Use serialVersionUID from JNDI 1.1.1 for interoperability. | 
|
*/  | 
|
private static final long serialVersionUID = 4980164073184639448L;  | 
|
}  |