001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package javax.naming.directory;
019:
020: import java.io.IOException;
021: import java.io.ObjectInputStream;
022: import java.io.ObjectOutputStream;
023: import java.util.Map;
024: import java.util.Vector;
025: import java.util.Iterator;
026: import java.util.Hashtable;
027: import java.util.Enumeration;
028: import javax.naming.NamingEnumeration;
029:
030: import org.apache.harmony.jndi.internal.nls.Messages;
031:
032: /**
033: * A simple implementation of the <code>Attributes</code> interface.
034: * <p>
035: * The <code>BasicAttributes</code> provides operations on any types of
036: * attribute. When a new attribute is created the <code>BasicAttributes</code>
037: * class will create a new <code>BasicAttribute</code> and add it to the
038: * attribute collection.
039: * </p>
040: * <p>
041: * A particular instance of <code>BasicAttributes</code> can be either
042: * case-sensitive or case-insensitive, as defined by the <code>isCaseIgnored()
043: * </code>
044: * method.
045: * </p>
046: * <p>
047: * Note that changes to the <code>BasicAttributes</code> are local -- they do
048: * not modify the directory. The directory is only modified by API calls on the
049: * {@link DirContext} object.
050: * </p>
051: *
052: * @see Attributes
053: */
054: public class BasicAttributes implements Attributes {
055:
056: /*
057: * This constant is used during deserialization to check the version which
058: * created the serialized object.
059: */
060: static final long serialVersionUID = 0x451d18d6a95539d8L;
061:
062: /**
063: * Flag indicating whether the case of attribute identifier is ignored.
064: *
065: * @serial
066: */
067: private boolean ignoreCase;
068:
069: // A map, Id => Attribute
070: private transient Hashtable<String, Attribute> attrMap = new Hashtable<String, Attribute>();
071:
072: /**
073: * Constructs a <code>BasicAttributes</code> instance which is
074: * case-sensitive.
075: */
076: public BasicAttributes() {
077: this (false);
078: }
079:
080: /**
081: * Constructs a <code>BasicAttributes</code> instance which is
082: * case-sensitive if <code>flag</code> is false.
083: *
084: * @param flag
085: * Indicates whether this instance is case-insensitive.
086: */
087: public BasicAttributes(boolean flag) {
088: this .ignoreCase = flag;
089: }
090:
091: /**
092: * Constructs a case-sensitive <code>BasicAttributes</code> instance with
093: * one attribute.
094: *
095: * @param attrId
096: * the ID of the first attribute
097: * @param attrObj
098: * the value of the first attribute
099: */
100: public BasicAttributes(String attrId, Object attrObj) {
101: this (attrId, attrObj, false);
102: }
103:
104: /**
105: * Constructs a <code>BasicAttributes</code> instance with one attribute
106: * which is case-sensitive if <code>flag</code> is false.
107: *
108: * @param attrId
109: * the ID of the first attribute
110: * @param attrObj
111: * the value of the first attribute
112: * @param flag
113: * Indicates whether this instance is case-insensitive.
114: */
115: public BasicAttributes(String attrId, Object attrObj, boolean flag) {
116: this .ignoreCase = flag;
117: this .attrMap.put(convertId(attrId), new BasicAttribute(attrId,
118: attrObj));
119: }
120:
121: /*
122: * Convert an attribute ID to lower case if this attribute collection is
123: * case-insensitive.
124: */
125: private String convertId(String id) {
126: return ignoreCase ? id.toLowerCase() : id;
127: }
128:
129: public Attribute get(String id) {
130: return attrMap.get(convertId(id));
131: }
132:
133: public NamingEnumeration<Attribute> getAll() {
134: return new BasicNamingEnumeration<Attribute>(attrMap.elements());
135: }
136:
137: public NamingEnumeration<String> getIDs() {
138: if (ignoreCase) {
139: Enumeration<Attribute> e = this .attrMap.elements();
140: Vector<String> v = new Vector<String>(attrMap.size());
141:
142: while (e.hasMoreElements()) {
143: v.add((e.nextElement()).getID());
144: }
145:
146: return new BasicNamingEnumeration<String>(v.elements());
147: }
148: return new BasicNamingEnumeration<String>(this .attrMap.keys());
149: }
150:
151: public boolean isCaseIgnored() {
152: return ignoreCase;
153: }
154:
155: public Attribute put(Attribute attribute) {
156: String id = convertId(attribute.getID());
157: return attrMap.put(id, attribute);
158: }
159:
160: public Attribute put(String id, Object obj) {
161: return put(new BasicAttribute(id, obj));
162: }
163:
164: public Attribute remove(String id) {
165: return attrMap.remove(convertId(id));
166: }
167:
168: public int size() {
169: return attrMap.size();
170: }
171:
172: /*
173: * Serialization of the <code>BasicAttributes</code> class is as follows:
174: * ignore attribute case (boolean) number of attributes (int) list of
175: * attribute objects
176: */
177: private void readObject(ObjectInputStream ois) throws IOException,
178: ClassNotFoundException {
179: int size;
180:
181: ois.defaultReadObject();
182: size = ois.readInt();
183: attrMap = new Hashtable<String, Attribute>();
184: for (int i = 0; i < size; i++) {
185: BasicAttribute attr = (BasicAttribute) ois.readObject();
186: attrMap.put(convertId(attr.getID()), attr);
187: }
188: }
189:
190: /*
191: * Serialization of the <code>BasicAttributes</code> class is as follows:
192: * ignore attribute case (boolean) number of attributes (int) list of
193: * attribute objects
194: */
195: private void writeObject(ObjectOutputStream oos) throws IOException {
196: oos.defaultWriteObject();
197: oos.writeInt(attrMap.size());
198: for (Enumeration<Attribute> enumeration = attrMap.elements(); enumeration
199: .hasMoreElements();) {
200: oos.writeObject(enumeration.nextElement());
201: }
202: }
203:
204: /**
205: * Returns a deep copy of this attribute collection. The returned copy
206: * contains the same attribute objects. The attribute objects are not
207: * cloned.
208: *
209: * @return a deep copy of this attribute collection
210: */
211: @SuppressWarnings("unchecked")
212: @Override
213: public Object clone() {
214: try {
215: BasicAttributes c = (BasicAttributes) super .clone();
216: c.attrMap = (Hashtable<String, Attribute>) this .attrMap
217: .clone();
218: return c;
219: } catch (CloneNotSupportedException e) {
220: // jndi.15=Failed to clone object of BasicAttributes class.
221: throw new AssertionError(Messages.getString("jndi.15")); //$NON-NLS-1$
222: }
223: }
224:
225: /**
226: * Returns true if this <code>BasicAttributes</code> instance is equal to
227: * the supplied object <code>obj</code>. They are considered equal if
228: * they handle case the same way and have equal attributes.
229: * <code>Attribute</code> equality is tested by calling <code>
230: * equals</code>
231: * on each attribute, which may be overridden.
232: *
233: * @param obj
234: * the object to compare with
235: * @return true if this object is equal to <code>obj</code>, otherwise
236: * false
237: */
238: @Override
239: public boolean equals(Object obj) {
240: if (!(obj instanceof Attributes)) {
241: return false;
242: }
243:
244: // compare case & size
245: Attributes o = (Attributes) obj;
246: if (isCaseIgnored() != o.isCaseIgnored() || size() != o.size()) {
247: return false;
248: }
249:
250: // compare each attribute
251: Iterator<Map.Entry<String, Attribute>> it = attrMap.entrySet()
252: .iterator();
253: while (it.hasNext()) {
254: Map.Entry<String, Attribute> e = it.next();
255: if (!e.getValue().equals(o.get(e.getKey()))) {
256: return false;
257: }
258: }
259:
260: return true;
261: }
262:
263: /**
264: * Returns the hashcode for this <code>BasicAttributes</code> instance.
265: * The result is calculated by summing the hashcodes of all attributes,
266: * incremented by one if this instance is not case-sensitive.
267: *
268: * @return the hashcode of this <code>BasicAttributes</code> instance
269: */
270: @Override
271: public int hashCode() {
272: Enumeration<Attribute> e = attrMap.elements();
273: int i = (ignoreCase ? 1 : 0);
274:
275: while (e.hasMoreElements()) {
276: i += e.nextElement().hashCode();
277: }
278:
279: return i;
280: }
281:
282: /**
283: * Returns the string representation of this <code>BasicAttributes</code>
284: * instance. The result contains the attribute identifiers and values'
285: * string representations.
286: *
287: * @return the string representation of this object
288: */
289: @Override
290: public String toString() {
291: String s = null;
292: Iterator<Map.Entry<String, Attribute>> it = attrMap.entrySet()
293: .iterator();
294: Map.Entry<String, Attribute> e = null;
295:
296: if (it.hasNext()) {
297: // If there are one or more attributes, print them all.
298: e = it.next();
299: s = "{\n"; //$NON-NLS-1$
300: s += e.getKey();
301: s += "=" + e.getValue().toString(); //$NON-NLS-1$
302: while (it.hasNext()) {
303: e = it.next();
304: s += "; "; //$NON-NLS-1$
305: s += e.getKey();
306: s += "=" + e.getValue().toString(); //$NON-NLS-1$
307: }
308: s += "}\n"; //$NON-NLS-1$
309: } else {
310: // Otherwise, print an indication that no attributes are stored.
311: s = "This Attributes does not have any attributes.\n"; //$NON-NLS-1$
312: }
313: return s;
314: }
315: }
|