View Javadoc

1   /*
2    * Copyright 2002-2005 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package net.sf.ldaptemplate.support;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Hashtable;
22  import java.util.Iterator;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.SortedSet;
26  import java.util.TreeSet;
27  
28  import javax.naming.Context;
29  import javax.naming.Name;
30  import javax.naming.NameNotFoundException;
31  import javax.naming.NameParser;
32  import javax.naming.NamingEnumeration;
33  import javax.naming.NamingException;
34  import javax.naming.directory.Attribute;
35  import javax.naming.directory.Attributes;
36  import javax.naming.directory.BasicAttribute;
37  import javax.naming.directory.BasicAttributes;
38  import javax.naming.directory.DirContext;
39  import javax.naming.directory.ModificationItem;
40  import javax.naming.directory.SearchControls;
41  
42  import net.sf.ldaptemplate.DefaultNamingExceptionTranslator;
43  import net.sf.ldaptemplate.NamingExceptionTranslator;
44  
45  import org.apache.commons.collections.CollectionUtils;
46  import org.apache.commons.lang.ArrayUtils;
47  import org.apache.commons.lang.StringUtils;
48  import org.apache.commons.lang.builder.EqualsBuilder;
49  import org.apache.commons.lang.builder.HashCodeBuilder;
50  import org.apache.commons.logging.Log;
51  import org.apache.commons.logging.LogFactory;
52  
53  /***
54   * Implements the interesting methods of the DirContext interface. In particular
55   * it contains utility methods for getting and setting Attributes. Using the
56   * {@link net.sf.ldaptemplate.support.DefaultDirObjectFactory} in your
57   * ContextSource you may receive instances of this class from searches and
58   * lookups. This can be particularly useful when updating data, since this class
59   * implements {@link net.sf.ldaptemplate.support.AttributeModificationsAware},
60   * providing a {@link #getModificationItems()} method.
61   * 
62   * @author Magnus Robertsson
63   * @author Andreas Ronge
64   * @author Adam Skogman
65   * @author Mattias Arthursson
66   */
67  public class DirContextAdapter implements DirContextOperations {
68  
69      private static final boolean ORDER_DOESNT_MATTER = false;
70  
71      private static Log log = LogFactory.getLog(DirContextAdapter.class);
72  
73      private final Attributes attrs;
74  
75      private Name dn;
76  
77      private boolean updateMode = false;
78  
79      private Attributes updatedAttrs;
80  
81      private NamingExceptionTranslator exceptionTranslator;
82  
83      /***
84       * Default constructor.
85       */
86      public DirContextAdapter() {
87          attrs = new BasicAttributes(true);
88          dn = null;
89      }
90  
91      public DirContextAdapter(Name dn) {
92          attrs = new BasicAttributes(true);
93          this.dn = dn;
94      }
95  
96      /***
97       * Create a new entry from the supplied attributes and dn.
98       * 
99       * @param pAttrs
100      *            the attributes.
101      * @param dn
102      *            the dn.
103      */
104     public DirContextAdapter(Attributes pAttrs, Name dn) {
105         attrs = (Attributes) pAttrs.clone();
106         this.dn = dn;
107     }
108 
109     /***
110      * Constructor for cloning an existing entry.
111      * 
112      * @param master
113      *            The object to be copied.
114      */
115     protected DirContextAdapter(DirContextAdapter master) {
116         this.attrs = (Attributes) master.attrs.clone();
117         this.dn = master.dn;
118         this.updatedAttrs = (Attributes) master.updatedAttrs.clone();
119         this.updateMode = master.updateMode;
120     }
121 
122     /***
123      * Sets the update mode. The update mode should be <code>false</code> for
124      * a new entry and <code>true</code> for an existing entry that is being
125      * updated.
126      * 
127      * @param mode
128      *            Update mode.
129      */
130     protected void setUpdateMode(boolean mode) {
131         this.updateMode = mode;
132         if (updateMode) {
133             updatedAttrs = new BasicAttributes(true);
134         }
135     }
136 
137     /*
138      * (non-Javadoc)
139      * 
140      * @see net.sf.ldaptemplate.support.DirContextOperations#isUpdateMode()
141      */
142     public boolean isUpdateMode() {
143         return updateMode;
144     }
145 
146     /*
147      * (non-Javadoc)
148      * 
149      * @see net.sf.ldaptemplate.support.DirContextOperations#getNamesOfModifiedAttributes()
150      */
151     public String[] getNamesOfModifiedAttributes() {
152 
153         List tmpList = new ArrayList();
154 
155         NamingEnumeration attributesEnumeration;
156         if (isUpdateMode()) {
157             attributesEnumeration = updatedAttrs.getAll();
158         } else {
159             attributesEnumeration = attrs.getAll();
160         }
161 
162         try {
163             while (attributesEnumeration.hasMore()) {
164                 Attribute oneAttribute = (Attribute) attributesEnumeration
165                         .next();
166                 tmpList.add(oneAttribute.getID());
167             }
168         } catch (NamingException e) {
169             throw getExceptionTranslator().translate(e);
170         } finally {
171             closeNamingEnumeration(attributesEnumeration);
172         }
173 
174         return (String[]) tmpList.toArray(new String[0]);
175     }
176 
177     private void closeNamingEnumeration(NamingEnumeration enumeration) {
178         try {
179             if (enumeration != null) {
180                 enumeration.close();
181             }
182         } catch (NamingException e) {
183             // Never mind this
184         }
185     }
186 
187     /*
188      * @see net.sf.ldaptemplate.support.AttributeModificationsAware#getModificationItems()
189      */
190     public ModificationItem[] getModificationItems() {
191         if (!updateMode) {
192             return new ModificationItem[0];
193         }
194 
195         List tmpList = new LinkedList();
196         NamingEnumeration attributesEnumeration = null;
197         try {
198             attributesEnumeration = updatedAttrs.getAll();
199 
200             // find attributes that have been changed, removed or added
201             while (attributesEnumeration.hasMore()) {
202                 Attribute oneAttr = (Attribute) attributesEnumeration.next();
203 
204                 collectModifications(oneAttr, tmpList);
205             }
206         } catch (NamingException e) {
207             throw getExceptionTranslator().translate(e);
208         } finally {
209             closeNamingEnumeration(attributesEnumeration);
210         }
211 
212         if (log.isDebugEnabled()) {
213             log.debug("Number of modifications:" + tmpList.size());
214         }
215 
216         return (ModificationItem[]) tmpList
217                 .toArray(new ModificationItem[tmpList.size()]);
218     }
219 
220     /***
221      * Collect all modifications for the changed attribute. If no changes have
222      * been made, return immediately. If modifications have been made, and the
223      * original size as well as the updated size of the attribute is 1, replace
224      * the attribute. If the size of the updated attribute is 0, remove the
225      * attribute. Otherwise, the attribute is a multi-value attribute, in which
226      * case all modifications to the original value (removals and additions)
227      * will be collected individually.
228      * 
229      * @param changedAttr
230      *            the value of the changed attribute.
231      * @param modificationList
232      *            the list in which to add the modifications.
233      * @throws NamingException
234      *             if thrown by called Attribute methods.
235      */
236     private void collectModifications(Attribute changedAttr,
237             List modificationList) throws NamingException {
238         Attribute currentAttribute = attrs.get(changedAttr.getID());
239 
240         if (changedAttr.equals(currentAttribute)) {
241             // No changes
242             return;
243         } else if (currentAttribute != null && currentAttribute.size() == 1
244                 && changedAttr.size() == 1) {
245             // Replace single-vale attribute.
246             modificationList.add(new ModificationItem(
247                     DirContext.REPLACE_ATTRIBUTE, changedAttr));
248         } else if (changedAttr.size() == 0) {
249             // Attribute has been removed.
250             modificationList.add(new ModificationItem(
251                     DirContext.REMOVE_ATTRIBUTE, changedAttr));
252         } else {
253             // Collect all modifications to attribute individually (this also
254             // covers additions to a previously non-existant attribute).
255             Collection oldValues = new LinkedList();
256             Collection newValues = new LinkedList();
257 
258             collectAttributeValues(oldValues, currentAttribute);
259             collectAttributeValues(newValues, changedAttr);
260             Collection myModifications = new LinkedList();
261 
262             Collection addedValues = CollectionUtils.subtract(newValues,
263                     oldValues);
264             Collection removedValues = CollectionUtils.subtract(oldValues,
265                     newValues);
266 
267             collectModifications(DirContext.ADD_ATTRIBUTE, changedAttr,
268                     addedValues, myModifications);
269             collectModifications(DirContext.REMOVE_ATTRIBUTE, changedAttr,
270                     removedValues, myModifications);
271 
272             if (myModifications.isEmpty()) {
273                 // This means that the attributes are not equal, but the
274                 // actual values are the same - thus the order must have
275                 // changed. This should result in a REPLACE_ATTRIBUTE operation.
276                 myModifications.add(new ModificationItem(
277                         DirContext.REPLACE_ATTRIBUTE, changedAttr));
278             }
279 
280             modificationList.addAll(myModifications);
281         }
282     }
283 
284     private void collectModifications(int modificationType, Attribute attr,
285             Collection values, Collection c) {
286         if (values.size() > 0) {
287             BasicAttribute modificationAttribute = new BasicAttribute(attr
288                     .getID());
289             for (Iterator iter = values.iterator(); iter.hasNext();) {
290                 modificationAttribute.add(iter.next());
291             }
292             c
293                     .add(new ModificationItem(modificationType,
294                             modificationAttribute));
295         }
296     }
297 
298     private void collectAttributeValues(Collection valueCollection,
299             Attribute attribute) throws NamingException {
300 
301         if (attribute == null) {
302             return;
303         }
304 
305         NamingEnumeration attributeValues = attribute.getAll();
306         while (attributeValues.hasMoreElements()) {
307             Object value = (Object) attributeValues.nextElement();
308             valueCollection.add(value);
309         }
310     }
311 
312     /***
313      * Decide whether an attribute has changed or not.
314      * 
315      * @param name
316      *            Attribute name.
317      * @param value
318      *            Attribute value.
319      * @return <code>true</code> if attribute has changed.
320      */
321     private boolean isChanged(String name, Object value) {
322         Attribute a = attrs.get(name);
323 
324         // FALSE if both are null it is not changed
325         if (a == null && value == null) {
326             return false;
327         }
328 
329         // TRUE if existing value is null or does not contain one value
330         if (a == null || a.size() != 1) {
331             return true;
332         }
333 
334         // TRUE if existing value is not null and the new one is null
335         if (a != null && value == null) {
336             return true;
337         }
338 
339         // TRUE if we can't access the value
340         Object obj = null;
341         try {
342             obj = a.get(0);
343         } catch (NamingException e) {
344             return true;
345         }
346 
347         // TRUE if the value is not equal and all other tests has been performed
348         return !value.equals(obj);
349     }
350 
351     /***
352      * returns true if the attribute is empty. It is empty if a == null, size ==
353      * 0 or get() == null or an exception if thrown when accessing the get
354      * method
355      */
356     private boolean isEmptyAttribute(Attribute a) {
357         try {
358             return (a == null || a.size() == 0 || a.get() == null);
359         } catch (NamingException e) {
360             return true;
361         }
362     }
363 
364     /***
365      * Compare an existing attribute with name pName with value pValue. The
366      * order of the array must be the same order as the existing multivalued
367      * attribute.
368      * 
369      * @param name
370      * @param values
371      * @return true if it has changed
372      */
373     private boolean isChanged(String name, Object[] values, boolean orderMatters) {
374 
375         Attribute a = attrs.get(name);
376 
377         // values == null and values.length == 0 is treated the same way
378         boolean emptyNewValue = (values == null || values.length == 0);
379 
380         // Setting to empty ---------------------
381         if (emptyNewValue) {
382             // FALSE if both are null it is not changed (they both does not
383             // exist)
384             // TRUE if new value is null and old value exists (should be
385             // removed)
386             return (a != null);
387         }
388 
389         // NOT setting to empty -------------------
390 
391         // TRUE if existing value is null
392         if (a == null) {
393             return true;
394         }
395 
396         // TRUE is different length
397         if (a.size() != values.length) {
398             return true;
399         }
400 
401         // Check contents of arrays
402 
403         // Order DOES matter, e.g. first names
404         try {
405             for (int i = 0; i < a.size(); i++) {
406                 Object obj = a.get(i);
407                 // TRUE if one value is not equal
408                 if (!(obj instanceof String)) {
409                     return true;
410                 }
411                 if (orderMatters) {
412                     // check only the string with same index
413                     if (!values[i].equals(obj)) {
414                         return true;
415                     }
416                 } else {
417                     // check all strings
418                     if (!ArrayUtils.contains(values, obj)) {
419                         return true;
420                     }
421                 }
422             }
423 
424         } catch (NamingException e) {
425             // TRUE if we can't access the value
426             return true;
427         }
428 
429         // FALSE since we have compared all values
430         return false;
431     }
432 
433     /***
434      * Checks if an entry has a specific attribute.
435      * 
436      * This method simply calls exists(String) with the attribute name.
437      * 
438      * @param attr
439      *            the attribute to check.
440      * @return true if attribute exists in entry.
441      */
442     protected final boolean exists(Attribute attr) {
443         return exists(attr.getID());
444     }
445 
446     /***
447      * Checks if the attribute exists in this entry, either it was read or it
448      * has been added and update() has been called.
449      * 
450      * @param attrId
451      *            id of the attribute to check.
452      * @return true if the attribute exists in the entry.
453      */
454     protected final boolean exists(String attrId) {
455         return attrs.get(attrId) != null;
456     }
457 
458     /*
459      * (non-Javadoc)
460      * 
461      * @see net.sf.ldaptemplate.support.DirContextOperations#getStringAttribute(java.lang.String)
462      */
463     public String getStringAttribute(String name) {
464         return (String) getObjectAttribute(name);
465     }
466 
467     /*
468      * (non-Javadoc)
469      * 
470      * @see net.sf.ldaptemplate.support.DirContextOperations#getObjectAttribute(java.lang.String)
471      */
472     public Object getObjectAttribute(String name) {
473         Attribute oneAttr = attrs.get(name);
474         if (oneAttr == null) {
475             return null;
476         }
477         try {
478             return oneAttr.get();
479         } catch (NamingException e) {
480             throw getExceptionTranslator().translate(e);
481         }
482     }
483 
484     /*
485      * (non-Javadoc)
486      * 
487      * @see net.sf.ldaptemplate.support.DirContextOperations#setAttributeValue(java.lang.String,
488      *      java.lang.Object)
489      */
490     public void setAttributeValue(String name, Object value) {
491         // new entry
492         if (!updateMode && value != null) {
493             attrs.put(name, value);
494         }
495 
496         // updating entry
497         if (updateMode && isChanged(name, value)) {
498             BasicAttribute attribute = new BasicAttribute(name);
499             if (value != null) {
500                 attribute.add(value);
501             }
502             updatedAttrs.put(attribute);
503         }
504 
505     }
506 
507     /*
508      * (non-Javadoc)
509      * 
510      * @see net.sf.ldaptemplate.support.DirContextOperations#setAttributeValues(java.lang.String,
511      *      java.lang.Object[])
512      */
513     public void setAttributeValues(String name, Object[] values) {
514         setAttributeValues(name, values, ORDER_DOESNT_MATTER);
515     }
516 
517     /*
518      * (non-Javadoc)
519      * 
520      * @see net.sf.ldaptemplate.support.DirContextOperations#setAttributeValues(java.lang.String,
521      *      java.lang.Object[], boolean)
522      */
523     public void setAttributeValues(String name, Object[] values,
524             boolean orderMatters) {
525         Attribute a = new BasicAttribute(name, orderMatters);
526 
527         for (int i = 0; values != null && i < values.length; i++) {
528             a.add(values[i]);
529         }
530 
531         // only change the original attribute if not in update mode
532         if (!updateMode && values != null && values.length > 0) {
533             // don't save empty arrays
534             attrs.put(a);
535         }
536 
537         // possible to set an already existing attribute to an empty array
538         if (updateMode && isChanged(name, values, orderMatters)) {
539             updatedAttrs.put(a);
540         }
541     }
542 
543     /*
544      * (non-Javadoc)
545      * 
546      * @see net.sf.ldaptemplate.support.DirContextOperations#update()
547      */
548     public void update() {
549         NamingEnumeration attributesEnumeration = null;
550 
551         try {
552             attributesEnumeration = updatedAttrs.getAll();
553 
554             // find what to update
555             while (attributesEnumeration.hasMore()) {
556                 Attribute a = (Attribute) attributesEnumeration.next();
557 
558                 // if it does not exist it should be added
559                 if (isEmptyAttribute(a)) {
560                     attrs.remove(a.getID());
561                 } else {
562                     // Otherwise it should be set.
563                     attrs.put(a);
564                 }
565             }
566         } catch (NamingException e) {
567             throw getExceptionTranslator().translate(e);
568         } finally {
569             closeNamingEnumeration(attributesEnumeration);
570         }
571 
572         // Reset the attributes to be updated
573         updatedAttrs = new BasicAttributes(true);
574     }
575 
576     /*
577      * (non-Javadoc)
578      * 
579      * @see net.sf.ldaptemplate.support.DirContextOperations#getStringAttributes(java.lang.String)
580      */
581     public String[] getStringAttributes(String name) {
582         String[] attributes;
583 
584         Attribute attribute = attrs.get(name);
585         if (attribute != null && attribute.size() > 0) {
586             attributes = new String[attribute.size()];
587             for (int i = 0; i < attribute.size(); i++) {
588                 try {
589                     attributes[i] = (String) attribute.get(i);
590                 } catch (NamingException e) {
591                     throw getExceptionTranslator().translate(e);
592                 }
593             }
594         } else {
595             return null;
596         }
597 
598         return attributes;
599     }
600 
601     /*
602      * (non-Javadoc)
603      * 
604      * @see net.sf.ldaptemplate.support.DirContextOperations#getAttributeSortedStringSet(java.lang.String)
605      */
606     public SortedSet getAttributeSortedStringSet(String name) {
607         TreeSet attrSet = new TreeSet();
608 
609         Attribute attribute = attrs.get(name);
610         if (attribute != null) {
611             for (int i = 0; i < attribute.size(); i++) {
612                 try {
613                     attrSet.add(attribute.get(i));
614                 } catch (NamingException e) {
615                     throw getExceptionTranslator().translate(e);
616                 }
617             }
618         } else {
619             return null;
620         }
621 
622         return attrSet;
623     }
624 
625     /***
626      * Set the supplied attribute.
627      * 
628      * @param attribute
629      *            the attribute to set.
630      */
631     public void setAttribute(Attribute attribute) {
632         attrs.put(attribute);
633     }
634 
635     /***
636      * Get all attributes.
637      * 
638      * @return all attributes.
639      */
640     public Attributes getAttributes() {
641         return attrs;
642     }
643 
644     /***
645      * @see javax.naming.directory.DirContext#getAttributes(Name)
646      */
647     public Attributes getAttributes(Name name) throws NamingException {
648         return getAttributes(name.toString());
649     }
650 
651     /***
652      * @see javax.naming.directory.DirContext#getAttributes(String)
653      */
654     public Attributes getAttributes(String name) throws NamingException {
655         if (!StringUtils.isEmpty(name)) {
656             throw new NameNotFoundException();
657         }
658         return (Attributes) attrs.clone();
659     }
660 
661     /***
662      * @see javax.naming.directory.DirContext#getAttributes(Name, String[])
663      */
664     public Attributes getAttributes(Name name, String[] attrIds)
665             throws NamingException {
666         return getAttributes(name.toString(), attrIds);
667     }
668 
669     /***
670      * @see javax.naming.directory.DirContext#getAttributes(String, String[])
671      */
672     public Attributes getAttributes(String name, String[] attrIds)
673             throws NamingException {
674         if (!StringUtils.isEmpty(name)) {
675             throw new NameNotFoundException();
676         }
677 
678         Attributes a = new BasicAttributes(true);
679         Attribute target;
680         for (int i = 0; i < attrIds.length; i++) {
681             target = attrs.get(attrIds[i]);
682             if (target != null) {
683                 a.put(target);
684             }
685         }
686 
687         return a;
688     }
689 
690     /***
691      * @see javax.naming.directory.DirContext#modifyAttributes(javax.naming.Name,
692      *      int, javax.naming.directory.Attributes)
693      */
694     public void modifyAttributes(Name name, int modOp, Attributes attrs)
695             throws NamingException {
696         throw new UnsupportedOperationException("Not implemented.");
697     }
698 
699     /***
700      * @see javax.naming.directory.DirContext#modifyAttributes(String, int,
701      *      Attributes)
702      */
703     public void modifyAttributes(String name, int modOp, Attributes attrs)
704             throws NamingException {
705         throw new UnsupportedOperationException("Not implemented.");
706     }
707 
708     /***
709      * @see javax.naming.directory.DirContext#modifyAttributes(Name,
710      *      ModificationItem[])
711      */
712     public void modifyAttributes(Name name, ModificationItem[] mods)
713             throws NamingException {
714         throw new UnsupportedOperationException("Not implemented.");
715     }
716 
717     /***
718      * @see javax.naming.directory.DirContext#modifyAttributes(String,
719      *      ModificationItem[])
720      */
721     public void modifyAttributes(String name, ModificationItem[] mods)
722             throws NamingException {
723         throw new UnsupportedOperationException("Not implemented.");
724     }
725 
726     /***
727      * @see javax.naming.directory.DirContext#bind(Name, Object, Attributes)
728      */
729     public void bind(Name name, Object obj, Attributes attrs)
730             throws NamingException {
731         throw new UnsupportedOperationException("Not implemented.");
732     }
733 
734     /***
735      * @see javax.naming.directory.DirContext#bind(String, Object, Attributes)
736      */
737     public void bind(String name, Object obj, Attributes attrs)
738             throws NamingException {
739         throw new UnsupportedOperationException("Not implemented.");
740     }
741 
742     /***
743      * @see javax.naming.directory.DirContext#rebind(Name, Object, Attributes)
744      */
745     public void rebind(Name name, Object obj, Attributes attrs)
746             throws NamingException {
747         throw new UnsupportedOperationException("Not implemented.");
748     }
749 
750     /***
751      * @see javax.naming.directory.DirContext#rebind(String, Object, Attributes)
752      */
753     public void rebind(String name, Object obj, Attributes attrs)
754             throws NamingException {
755         throw new UnsupportedOperationException("Not implemented.");
756     }
757 
758     /***
759      * @see javax.naming.directory.DirContext#createSubcontext(Name, Attributes)
760      */
761     public DirContext createSubcontext(Name name, Attributes attrs)
762             throws NamingException {
763         throw new UnsupportedOperationException("Not implemented.");
764     }
765 
766     /***
767      * @see javax.naming.directory.DirContext#createSubcontext(String,
768      *      Attributes)
769      */
770     public DirContext createSubcontext(String name, Attributes attrs)
771             throws NamingException {
772         throw new UnsupportedOperationException("Not implemented.");
773     }
774 
775     /***
776      * @see javax.naming.directory.DirContext#getSchema(Name)
777      */
778     public DirContext getSchema(Name name) throws NamingException {
779         throw new UnsupportedOperationException("Not implemented.");
780     }
781 
782     /***
783      * @see javax.naming.directory.DirContext#getSchema(String)
784      */
785     public DirContext getSchema(String name) throws NamingException {
786         throw new UnsupportedOperationException("Not implemented.");
787     }
788 
789     /***
790      * @see javax.naming.directory.DirContext#getSchemaClassDefinition(Name)
791      */
792     public DirContext getSchemaClassDefinition(Name name)
793             throws NamingException {
794         throw new UnsupportedOperationException("Not implemented.");
795     }
796 
797     /***
798      * @see javax.naming.directory.DirContext#getSchemaClassDefinition(String)
799      */
800     public DirContext getSchemaClassDefinition(String name)
801             throws NamingException {
802         throw new UnsupportedOperationException("Not implemented.");
803     }
804 
805     /***
806      * @see javax.naming.directory.DirContext#search(Name, Attributes, String[])
807      */
808     public NamingEnumeration search(Name name, Attributes matchingAttributes,
809             String[] attributesToReturn) throws NamingException {
810         throw new UnsupportedOperationException("Not implemented.");
811     }
812 
813     /***
814      * @see javax.naming.directory.DirContext#search(String, Attributes,
815      *      String[])
816      */
817     public NamingEnumeration search(String name, Attributes matchingAttributes,
818             String[] attributesToReturn) throws NamingException {
819         throw new UnsupportedOperationException("Not implemented.");
820     }
821 
822     /***
823      * @see javax.naming.directory.DirContext#search(Name, Attributes)
824      */
825     public NamingEnumeration search(Name name, Attributes matchingAttributes)
826             throws NamingException {
827         throw new UnsupportedOperationException("Not implemented.");
828     }
829 
830     /***
831      * @see javax.naming.directory.DirContext#search(String, Attributes)
832      */
833     public NamingEnumeration search(String name, Attributes matchingAttributes)
834             throws NamingException {
835         throw new UnsupportedOperationException("Not implemented.");
836     }
837 
838     /***
839      * @see javax.naming.directory.DirContext#search(Name, String,
840      *      SearchControls)
841      */
842     public NamingEnumeration search(Name name, String filter,
843             SearchControls cons) throws NamingException {
844         throw new UnsupportedOperationException("Not implemented.");
845     }
846 
847     /***
848      * @see javax.naming.directory.DirContext#search(String, String,
849      *      SearchControls)
850      */
851     public NamingEnumeration search(String name, String filter,
852             SearchControls cons) throws NamingException {
853         throw new UnsupportedOperationException("Not implemented.");
854     }
855 
856     /***
857      * @see javax.naming.directory.DirContext#search(Name, String, Object[],
858      *      SearchControls)
859      */
860     public NamingEnumeration search(Name name, String filterExpr,
861             Object[] filterArgs, SearchControls cons) throws NamingException {
862         throw new UnsupportedOperationException("Not implemented.");
863     }
864 
865     /***
866      * @see javax.naming.directory.DirContext#search(String, String, Object[],
867      *      SearchControls)
868      */
869     public NamingEnumeration search(String name, String filterExpr,
870             Object[] filterArgs, SearchControls cons) throws NamingException {
871         throw new UnsupportedOperationException("Not implemented.");
872     }
873 
874     /***
875      * @see javax.naming.Context#lookup(Name)
876      */
877     public Object lookup(Name name) throws NamingException {
878         throw new UnsupportedOperationException("Not implemented.");
879     }
880 
881     /***
882      * @see javax.naming.Context#lookup(String)
883      */
884     public Object lookup(String name) throws NamingException {
885         throw new UnsupportedOperationException("Not implemented.");
886     }
887 
888     /***
889      * @see javax.naming.Context#bind(Name, Object)
890      */
891     public void bind(Name name, Object obj) throws NamingException {
892         throw new UnsupportedOperationException("Not implemented.");
893     }
894 
895     /***
896      * @see javax.naming.Context#bind(String, Object)
897      */
898     public void bind(String name, Object obj) throws NamingException {
899         throw new UnsupportedOperationException("Not implemented.");
900     }
901 
902     /***
903      * @see javax.naming.Context#rebind(Name, Object)
904      */
905     public void rebind(Name name, Object obj) throws NamingException {
906         throw new UnsupportedOperationException("Not implemented.");
907     }
908 
909     /***
910      * @see javax.naming.Context#rebind(String, Object)
911      */
912     public void rebind(String name, Object obj) throws NamingException {
913         throw new UnsupportedOperationException("Not implemented.");
914     }
915 
916     /***
917      * @see javax.naming.Context#unbind(Name)
918      */
919     public void unbind(Name name) throws NamingException {
920         throw new UnsupportedOperationException("Not implemented.");
921     }
922 
923     /***
924      * @see javax.naming.Context#unbind(String)
925      */
926     public void unbind(String name) throws NamingException {
927         throw new UnsupportedOperationException("Not implemented.");
928     }
929 
930     /***
931      * @see javax.naming.Context#rename(Name, Name)
932      */
933     public void rename(Name oldName, Name newName) throws NamingException {
934         throw new UnsupportedOperationException("Not implemented.");
935     }
936 
937     /***
938      * @see javax.naming.Context#rename(String, String)
939      */
940     public void rename(String oldName, String newName) throws NamingException {
941         throw new UnsupportedOperationException("Not implemented.");
942     }
943 
944     /***
945      * @see javax.naming.Context#list(Name)
946      */
947     public NamingEnumeration list(Name name) throws NamingException {
948         throw new UnsupportedOperationException("Not implemented.");
949     }
950 
951     /***
952      * @see javax.naming.Context#list(String)
953      */
954     public NamingEnumeration list(String name) throws NamingException {
955         throw new UnsupportedOperationException("Not implemented.");
956     }
957 
958     /***
959      * @see javax.naming.Context#listBindings(Name)
960      */
961     public NamingEnumeration listBindings(Name name) throws NamingException {
962         throw new UnsupportedOperationException("Not implemented.");
963     }
964 
965     /***
966      * @see javax.naming.Context#listBindings(String)
967      */
968     public NamingEnumeration listBindings(String name) throws NamingException {
969         throw new UnsupportedOperationException("Not implemented.");
970     }
971 
972     /***
973      * @see javax.naming.Context#destroySubcontext(Name)
974      */
975     public void destroySubcontext(Name name) throws NamingException {
976         throw new UnsupportedOperationException("Not implemented.");
977     }
978 
979     /***
980      * @see javax.naming.Context#destroySubcontext(String)
981      */
982     public void destroySubcontext(String name) throws NamingException {
983         throw new UnsupportedOperationException("Not implemented.");
984     }
985 
986     /***
987      * @see javax.naming.Context#createSubcontext(Name)
988      */
989     public Context createSubcontext(Name name) throws NamingException {
990         throw new UnsupportedOperationException("Not implemented.");
991     }
992 
993     /***
994      * @see javax.naming.Context#createSubcontext(String)
995      */
996     public Context createSubcontext(String name) throws NamingException {
997         throw new UnsupportedOperationException("Not implemented.");
998     }
999 
1000     /***
1001      * @see javax.naming.Context#lookupLink(Name)
1002      */
1003     public Object lookupLink(Name name) throws NamingException {
1004         throw new UnsupportedOperationException("Not implemented.");
1005     }
1006 
1007     /***
1008      * @see javax.naming.Context#lookupLink(String)
1009      */
1010     public Object lookupLink(String name) throws NamingException {
1011         throw new UnsupportedOperationException("Not implemented.");
1012     }
1013 
1014     /***
1015      * @see javax.naming.Context#getNameParser(Name)
1016      */
1017     public NameParser getNameParser(Name name) throws NamingException {
1018         throw new UnsupportedOperationException("Not implemented.");
1019     }
1020 
1021     /***
1022      * @see javax.naming.Context#getNameParser(String)
1023      */
1024     public NameParser getNameParser(String name) throws NamingException {
1025         throw new UnsupportedOperationException("Not implemented.");
1026     }
1027 
1028     /***
1029      * @see javax.naming.Context#composeName(Name, Name)
1030      */
1031     public Name composeName(Name name, Name prefix) throws NamingException {
1032         throw new UnsupportedOperationException("Not implemented.");
1033     }
1034 
1035     /***
1036      * @see javax.naming.Context#composeName(String, String)
1037      */
1038     public String composeName(String name, String prefix)
1039             throws NamingException {
1040         throw new UnsupportedOperationException("Not implemented.");
1041     }
1042 
1043     /***
1044      * @see javax.naming.Context#addToEnvironment(String, Object)
1045      */
1046     public Object addToEnvironment(String propName, Object propVal)
1047             throws NamingException {
1048         throw new UnsupportedOperationException("Not implemented.");
1049     }
1050 
1051     /***
1052      * @see javax.naming.Context#removeFromEnvironment(String)
1053      */
1054     public Object removeFromEnvironment(String propName) throws NamingException {
1055         throw new UnsupportedOperationException("Not implemented.");
1056     }
1057 
1058     /***
1059      * @see javax.naming.Context#getEnvironment()
1060      */
1061     public Hashtable getEnvironment() throws NamingException {
1062         throw new UnsupportedOperationException("Not implemented.");
1063     }
1064 
1065     /***
1066      * @see javax.naming.Context#close()
1067      */
1068     public void close() throws NamingException {
1069         throw new UnsupportedOperationException("Not implemented.");
1070     }
1071 
1072     /***
1073      * @see javax.naming.Context#getNameInNamespace()
1074      */
1075     public String getNameInNamespace() {
1076         return dn.toString();
1077     }
1078 
1079     /*
1080      * (non-Javadoc)
1081      * 
1082      * @see net.sf.ldaptemplate.support.DirContextOperations#getDn()
1083      */
1084     public Name getDn() {
1085         return dn;
1086     }
1087 
1088     /*
1089      * (non-Javadoc)
1090      * 
1091      * @see net.sf.ldaptemplate.support.DirContextOperations#setDn(javax.naming.Name)
1092      */
1093     public final void setDn(Name dn) {
1094         if (!updateMode) {
1095             this.dn = dn;
1096         }
1097     }
1098 
1099     /***
1100      * @see java.lang.Object#equals(java.lang.Object)
1101      */
1102     public boolean equals(Object obj) {
1103         // A subclass with identical values should NOT be considered equal.
1104         // EqualsBuilder in commons-lang cannot handle subclasses correctly.
1105         if (obj == null || obj.getClass() != this.getClass()) {
1106             return false;
1107         }
1108         return EqualsBuilder.reflectionEquals(this, obj);
1109     }
1110 
1111     /***
1112      * @see Object#hashCode()
1113      */
1114     public int hashCode() {
1115         return HashCodeBuilder.reflectionHashCode(this);
1116     }
1117 
1118     /***
1119      * @see java.lang.Object#toString()
1120      */
1121     public String toString() {
1122         StringBuffer buf = new StringBuffer();
1123         buf.append(getClass().getName());
1124         buf.append(":");
1125         if (dn != null) {
1126             buf.append(" dn=" + dn);
1127         }
1128         buf.append(" {");
1129 
1130         try {
1131             for (NamingEnumeration i = attrs.getAll(); i.hasMore();) {
1132                 Attribute attribute = (Attribute) i.next();
1133                 if (attribute.size() == 1) {
1134                     buf.append(attribute.getID());
1135                     buf.append('=');
1136                     buf.append(attribute.get());
1137                 } else {
1138                     for (int j = 0; j < attribute.size(); j++) {
1139                         if (j > 0) {
1140                             buf.append(", ");
1141                         }
1142                         buf.append(attribute.getID());
1143                         buf.append('[');
1144                         buf.append(j);
1145                         buf.append("]=");
1146                         buf.append(attribute.get(j));
1147                     }
1148                 }
1149 
1150                 if (i.hasMore()) {
1151                     buf.append(", ");
1152                 }
1153             }
1154         } catch (NamingException e) {
1155             log.warn("Error in toString()");
1156         }
1157         buf.append('}');
1158 
1159         return buf.toString();
1160     }
1161 
1162     /***
1163      * Get the NamingExceptionTranslator.
1164      * 
1165      * @return the NamingExceptionTranslator to use; if none is specified,
1166      *         {@link DefaultNamingExceptionTranslator} is used.
1167      */
1168     public NamingExceptionTranslator getExceptionTranslator() {
1169         if (exceptionTranslator == null) {
1170             exceptionTranslator = new DefaultNamingExceptionTranslator();
1171         }
1172         return exceptionTranslator;
1173     }
1174 
1175     /***
1176      * Set the NamingExceptionTranslator to use.
1177      * 
1178      * @param exceptionTranslator
1179      *            the NamingExceptionTranslator to use.
1180      */
1181     public void setExceptionTranslator(
1182             NamingExceptionTranslator exceptionTranslator) {
1183         this.exceptionTranslator = exceptionTranslator;
1184     }
1185 
1186 }