001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.directory.shared.ldap.entry.client;
020
021
022 import java.io.IOException;
023 import java.io.ObjectInput;
024 import java.io.ObjectOutput;
025 import java.util.ArrayList;
026 import java.util.Collections;
027 import java.util.HashMap;
028 import java.util.Iterator;
029 import java.util.List;
030 import java.util.Map;
031 import java.util.SortedMap;
032 import java.util.TreeMap;
033
034 import javax.naming.NamingException;
035
036 import org.apache.directory.shared.ldap.entry.AbstractEntry;
037 import org.apache.directory.shared.ldap.entry.Entry;
038 import org.apache.directory.shared.ldap.entry.EntryAttribute;
039 import org.apache.directory.shared.ldap.entry.Value;
040 import org.apache.directory.shared.ldap.name.LdapDN;
041 import org.apache.directory.shared.ldap.util.StringTools;
042 import org.slf4j.Logger;
043 import org.slf4j.LoggerFactory;
044
045
046 /**
047 * A default implementation of a ServerEntry which should suite most
048 * use cases.
049 *
050 * This class is final, it should not be extended.
051 *
052 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
053 * @version $Rev$, $Date$
054 */
055 public final class DefaultClientEntry extends AbstractEntry<String> implements ClientEntry
056 {
057 /** Used for serialization */
058 private static final long serialVersionUID = 2L;
059
060 /** The logger for this class */
061 private static final Logger LOG = LoggerFactory.getLogger( DefaultClientEntry.class );
062
063 //-------------------------------------------------------------------------
064 // Constructors
065 //-------------------------------------------------------------------------
066 /**
067 * Creates a new instance of DefaultClientEntry.
068 * <p>
069 * This entry <b>must</b> be initialized before being used !
070 */
071 public DefaultClientEntry()
072 {
073 dn = LdapDN.EMPTY_LDAPDN;
074 }
075
076
077 /**
078 * Creates a new instance of DefaultServerEntry, with a
079 * DN.
080 *
081 * @param dn The DN for this serverEntry. Can be null.
082 */
083 public DefaultClientEntry( LdapDN dn )
084 {
085 this.dn = dn;
086 }
087
088
089 /**
090 * Creates a new instance of DefaultServerEntry, with a
091 * DN and a list of IDs.
092 *
093 * @param dn The DN for this serverEntry. Can be null.
094 * @param upIds The list of attributes to create.
095 */
096 public DefaultClientEntry( LdapDN dn, String... upIds )
097 {
098 this.dn = dn;
099
100 for ( String upId:upIds )
101 {
102 // Add a new AttributeType without value
103 set( upId );
104 }
105 }
106
107
108 /**
109 * <p>
110 * Creates a new instance of DefaultClientEntry, with a
111 * DN and a list of EntryAttributes.
112 * </p>
113 *
114 * @param dn The DN for this serverEntry. Can be null
115 * @param attributes The list of attributes to create
116 */
117 public DefaultClientEntry( LdapDN dn, EntryAttribute... attributes )
118 {
119 this.dn = dn;
120
121 for ( EntryAttribute attribute:attributes )
122 {
123 if ( attribute == null )
124 {
125 continue;
126 }
127
128 // Store a new ClientAttribute
129 this.attributes.put( attribute.getId(), attribute );
130 }
131 }
132
133
134 //-------------------------------------------------------------------------
135 // Helper methods
136 //-------------------------------------------------------------------------
137 private String getId( String upId ) throws IllegalArgumentException
138 {
139 String id = StringTools.trim( StringTools.toLowerCase( upId ) );
140
141 // If empty, throw an error
142 if ( ( id == null ) || ( id.length() == 0 ) )
143 {
144 String message = "The attributeType ID should not be null or empty";
145 LOG.error( message );
146 throw new IllegalArgumentException( message );
147 }
148
149 return id;
150 }
151
152
153 //-------------------------------------------------------------------------
154 // Entry methods
155 //-------------------------------------------------------------------------
156 /**
157 * Add some Attributes to the current Entry.
158 *
159 * @param attributes The attributes to add
160 * @throws NamingException If we can't add any of the attributes
161 */
162 public void add( EntryAttribute... attributes ) throws NamingException
163 {
164 // Loop on all the added attributes
165 for ( EntryAttribute attribute:attributes )
166 {
167 // If the attribute already exist, we will add the new values.
168 if ( contains( attribute ) )
169 {
170 EntryAttribute existingAttr = get( attribute.getId() );
171
172 // Loop on all the values, and add them to the existing attribute
173 for ( Value<?> value:attribute )
174 {
175 existingAttr.add( value );
176 }
177 }
178 else
179 {
180 // Stores the attribute into the entry
181 this.attributes.put( attribute.getId(), (ClientAttribute)attribute );
182 }
183 }
184 }
185
186
187 /**
188 * Add an attribute (represented by its ID and binary values) into an entry.
189 *
190 * @param upId The attribute ID
191 * @param values The list of binary values to inject. It can be empty
192 * @throws NamingException If the attribute does not exist
193 */
194 public void add( String upId, byte[]... values ) throws NamingException
195 {
196 // First, transform the upID to a valid ID
197 String id = getId( upId );
198
199 // Now, check to see if we already have such an attribute
200 EntryAttribute attribute = attributes.get( id );
201
202 if ( attribute != null )
203 {
204 // This Attribute already exist, we add the values
205 // into it. (If the values already exists, they will
206 // not be added, but this is done in the add() method)
207 attribute.add( values );
208 attribute.setUpId( upId );
209 }
210 else
211 {
212 // We have to create a new Attribute and set the values
213 // and the upId
214 attributes.put( id, new DefaultClientAttribute( upId, values ) );
215 }
216 }
217
218
219 /**
220 * Add some String values to the current Entry.
221 *
222 * @param upId The user provided ID of the attribute we want to add
223 * some values to
224 * @param values The list of String values to add
225 * @throws NamingException If we can't add any of the values
226 */
227 public void add( String upId, String... values ) throws NamingException
228 {
229 // First, transform the upID to a valid ID
230 String id = getId( upId );
231
232 // Now, check to see if we already have such an attribute
233 EntryAttribute attribute = attributes.get( id );
234
235 if ( attribute != null )
236 {
237 // This Attribute already exist, we add the values
238 // into it. (If the values already exists, they will
239 // not be added, but this is done in the add() method)
240 attribute.add( values );
241 attribute.setUpId( upId );
242 }
243 else
244 {
245 // We have to create a new Attribute and set the values
246 // and the upId
247 attributes.put( id, new DefaultClientAttribute( upId, values ) );
248 }
249 }
250
251
252 /**
253 * Add an attribute (represented by its ID and Value values) into an entry.
254 *
255 * @param upId The attribute ID
256 * @param values The list of Value values to inject. It can be empty
257 * @throws NamingException If the attribute does not exist
258 */
259 public void add( String upId, Value<?>... values ) throws NamingException
260 {
261 // First, transform the upID to a valid ID
262 String id = getId( upId );
263
264 // Now, check to see if we already have such an attribute
265 EntryAttribute attribute = attributes.get( id );
266
267 if ( attribute != null )
268 {
269 // This Attribute already exist, we add the values
270 // into it. (If the values already exists, they will
271 // not be added, but this is done in the add() method)
272 attribute.add( values );
273 attribute.setUpId( upId );
274 }
275 else
276 {
277 // We have to create a new Attribute and set the values
278 // and the upId
279 attributes.put( id, new DefaultClientAttribute( upId, values ) );
280 }
281 }
282
283
284 /**
285 * Clone an entry. All the element are duplicated, so a modification on
286 * the original object won't affect the cloned object, as a modification
287 * on the cloned object has no impact on the original object
288 */
289 public Entry clone()
290 {
291 // First, clone the structure
292 DefaultClientEntry clone = (DefaultClientEntry)super.clone();
293
294 // Just in case ... Should *never* happen
295 if ( clone == null )
296 {
297 return null;
298 }
299
300 // An Entry has a DN and many attributes.
301 // First, clone the DN, if not null.
302 if ( dn != null )
303 {
304 clone.setDn( (LdapDN)dn.clone() );
305 }
306
307 // then clone the ClientAttribute Map.
308 clone.attributes = (Map<String, EntryAttribute>)(((HashMap<String, EntryAttribute>)attributes).clone());
309
310 // now clone all the attributes
311 clone.attributes.clear();
312
313 for ( EntryAttribute attribute:attributes.values() )
314 {
315 clone.attributes.put( attribute.getId(), attribute.clone() );
316 }
317
318 // We are done !
319 return clone;
320 }
321
322
323 /**
324 * <p>
325 * Checks if an entry contains a list of attributes.
326 * </p>
327 * <p>
328 * If the list is null or empty, this method will return <code>true</code>
329 * if the entry has no attribute, <code>false</code> otherwise.
330 * </p>
331 *
332 * @param attributes The Attributes to look for
333 * @return <code>true</code> if all the attributes are found within
334 * the entry, <code>false</code> if at least one of them is not present.
335 * @throws NamingException If the attribute does not exist
336 */
337 public boolean contains( EntryAttribute... attributes ) throws NamingException
338 {
339 for ( EntryAttribute attribute:attributes )
340 {
341 if ( attribute == null )
342 {
343 return this.attributes.size() == 0;
344 }
345
346 if ( !this.attributes.containsKey( attribute.getId() ) )
347 {
348 return false;
349 }
350 }
351
352 return true;
353 }
354
355
356 /**
357 * Checks if an entry contains a specific attribute
358 *
359 * @param attributes The Attributes to look for
360 * @return <code>true</code> if the attributes are found within the entry
361 * @throws NamingException If the attribute does not exist
362 */
363 public boolean contains( String upId ) throws NamingException
364 {
365 String id = getId( upId );
366
367 return attributes.containsKey( id );
368 }
369
370
371 /**
372 * Checks if an entry contains an attribute with some binary values.
373 *
374 * @param id The Attribute we are looking for.
375 * @param values The searched values.
376 * @return <code>true</code> if all the values are found within the attribute,
377 * false if at least one value is not present or if the ID is not valid.
378 */
379 public boolean contains( String upId, byte[]... values )
380 {
381 String id = getId( upId );
382
383 EntryAttribute attribute = attributes.get( id );
384
385 if ( attribute == null )
386 {
387 return false;
388 }
389
390 return attribute.contains( values );
391 }
392
393
394 /**
395 * Checks if an entry contains an attribute with some String values.
396 *
397 * @param id The Attribute we are looking for.
398 * @param values The searched values.
399 * @return <code>true</code> if all the values are found within the attribute,
400 * false if at least one value is not present or if the ID is not valid.
401 */
402 public boolean contains( String upId, String... values )
403 {
404 String id = getId( upId );
405
406 EntryAttribute attribute = attributes.get( id );
407
408 if ( attribute == null )
409 {
410 return false;
411 }
412
413 return attribute.contains( values );
414 }
415
416
417 /**
418 * Checks if an entry contains an attribute with some values.
419 *
420 * @param id The Attribute we are looking for.
421 * @param values The searched values.
422 * @return <code>true</code> if all the values are found within the attribute,
423 * false if at least one value is not present or if the ID is not valid.
424 */
425 public boolean contains( String upId, Value<?>... values )
426 {
427 String id = getId( upId );
428
429 EntryAttribute attribute = attributes.get( id );
430
431 if ( attribute == null )
432 {
433 return false;
434 }
435
436 return attribute.contains( values );
437 }
438
439
440 /**
441 * Checks if an entry contains some specific attributes.
442 *
443 * @param attributes The Attributes to look for.
444 * @return <code>true</code> if the attributes are all found within the entry.
445 */
446 public boolean containsAttribute( String... attributes )
447 {
448 for ( String attribute:attributes )
449 {
450 String id = getId( attribute );
451
452 if ( !this.attributes.containsKey( id ) )
453 {
454 return false;
455 }
456 }
457
458 return true;
459 }
460
461
462 /**
463 * <p>
464 * Returns the attribute with the specified alias. The return value
465 * is <code>null</code> if no match is found.
466 * </p>
467 * <p>An Attribute with an id different from the supplied alias may
468 * be returned: for example a call with 'cn' may in some implementations
469 * return an Attribute whose getId() field returns 'commonName'.
470 * </p>
471 *
472 * @param alias an aliased name of the attribute identifier
473 * @return the attribute associated with the alias
474 */
475 public EntryAttribute get( String alias )
476 {
477 try
478 {
479 String id = getId( alias );
480
481 return attributes.get( id );
482 }
483 catch( IllegalArgumentException iea )
484 {
485 LOG.error( "An exception has been raised while looking for attribute id {}''", alias );
486 return null;
487 }
488 }
489
490
491 /**
492 * <p>
493 * Put an attribute (represented by its ID and some binary values) into an entry.
494 * </p>
495 * <p>
496 * If the attribute already exists, the previous attribute will be
497 * replaced and returned.
498 * </p>
499 *
500 * @param upId The attribute ID
501 * @param values The list of binary values to put. It can be empty.
502 * @return The replaced attribute
503 */
504 public EntryAttribute put( String upId, byte[]... values )
505 {
506 // Get the normalized form of the ID
507 String id = getId( upId );
508
509 // Create a new attribute
510 ClientAttribute clientAttribute = new DefaultClientAttribute( upId, values );
511
512 // Replace the previous one, and return it back
513 return attributes.put( id, clientAttribute );
514 }
515
516
517 /**
518 * <p>
519 * Put an attribute (represented by its ID and some String values) into an entry.
520 * </p>
521 * <p>
522 * If the attribute already exists, the previous attribute will be
523 * replaced and returned.
524 * </p>
525 *
526 * @param upId The attribute ID
527 * @param values The list of String values to put. It can be empty.
528 * @return The replaced attribute
529 */
530 public EntryAttribute put( String upId, String... values )
531 {
532 // Get the normalized form of the ID
533 String id = getId( upId );
534
535 // Create a new attribute
536 ClientAttribute clientAttribute = new DefaultClientAttribute( upId, values );
537
538 // Replace the previous one, and return it back
539 return attributes.put( id, clientAttribute );
540 }
541
542
543 /**
544 * <p>
545 * Put an attribute (represented by its ID and some values) into an entry.
546 * </p>
547 * <p>
548 * If the attribute already exists, the previous attribute will be
549 * replaced and returned.
550 * </p>
551 *
552 * @param upId The attribute ID
553 * @param values The list of values to put. It can be empty.
554 * @return The replaced attribute
555 */
556 public EntryAttribute put( String upId, Value<?>... values )
557 {
558 // Get the normalized form of the ID
559 String id = getId( upId );
560
561 // Create a new attribute
562 ClientAttribute clientAttribute = new DefaultClientAttribute( upId, values );
563
564 // Replace the previous one, and return it back
565 return attributes.put( id, clientAttribute );
566 }
567
568
569 /**
570 * <p>
571 * Put some new ClientAttribute using the User Provided ID.
572 * No value is inserted.
573 * </p>
574 * <p>
575 * If an existing Attribute is found, it will be replaced by an
576 * empty attribute, and returned to the caller.
577 * </p>
578 *
579 * @param upIds The user provided IDs of the AttributeTypes to add.
580 * @return A list of replaced Attributes.
581 */
582 public List<EntryAttribute> set( String... upIds )
583 {
584 if ( upIds == null )
585 {
586 String message = "The AttributeType list should not be null";
587 LOG.error( message );
588 throw new IllegalArgumentException( message );
589 }
590
591 List<EntryAttribute> returnedClientAttributes = new ArrayList<EntryAttribute>();
592
593 // Now, loop on all the attributeType to add
594 for ( String upId:upIds )
595 {
596 String id = StringTools.trim( StringTools.toLowerCase( upId ) );
597
598 if ( id == null )
599 {
600 String message = "The AttributeType list should not contain null values";
601 LOG.error( message );
602 throw new IllegalArgumentException( message );
603 }
604
605 if ( attributes.containsKey( id ) )
606 {
607 // Add the removed serverAttribute to the list
608 returnedClientAttributes.add( attributes.remove( id ) );
609 }
610
611 ClientAttribute newAttribute = new DefaultClientAttribute( upId );
612 attributes.put( id, newAttribute );
613 }
614
615 return returnedClientAttributes;
616 }
617
618
619 /**
620 * <p>
621 * Places attributes in the attribute collection.
622 * </p>
623 * <p>If there is already an attribute with the same ID as any of the
624 * new attributes, the old ones are removed from the collection and
625 * are returned by this method. If there was no attribute with the
626 * same ID the return value is <code>null</code>.
627 *</p>
628 *
629 * @param attributes the attributes to be put
630 * @return the old attributes with the same OID, if exist; otherwise
631 * <code>null</code>
632 * @exception NamingException if the operation fails
633 */
634 public List<EntryAttribute> put( EntryAttribute... attributes ) throws NamingException
635 {
636 // First, get the existing attributes
637 List<EntryAttribute> previous = new ArrayList<EntryAttribute>();
638
639 for ( EntryAttribute attribute:attributes )
640 {
641 String id = attribute.getId();
642
643 if ( contains( id ) )
644 {
645 // Store the attribute and remove it from the list
646 previous.add( get( id ) );
647 this.attributes.remove( id );
648 }
649
650 // add the new one
651 this.attributes.put( id, (ClientAttribute)attribute );
652 }
653
654 // return the previous attributes
655 return previous;
656 }
657
658
659 public List<EntryAttribute> remove( EntryAttribute... attributes ) throws NamingException
660 {
661 List<EntryAttribute> removedAttributes = new ArrayList<EntryAttribute>();
662
663 for ( EntryAttribute attribute:attributes )
664 {
665 if ( contains( attribute.getId() ) )
666 {
667 this.attributes.remove( attribute.getId() );
668 removedAttributes.add( attribute );
669 }
670 }
671
672 return removedAttributes;
673 }
674
675
676 /**
677 * <p>
678 * Removes the attribute with the specified alias.
679 * </p>
680 * <p>
681 * The removed attribute are returned by this method.
682 * </p>
683 * <p>
684 * If there is no attribute with the specified alias,
685 * the return value is <code>null</code>.
686 * </p>
687 *
688 * @param attributes an aliased name of the attribute to be removed
689 * @return the removed attributes, if any, as a list; otherwise <code>null</code>
690 */
691 public List<EntryAttribute> removeAttributes( String... attributes )
692 {
693 if ( attributes.length == 0 )
694 {
695 return null;
696 }
697
698 List<EntryAttribute> removed = new ArrayList<EntryAttribute>( attributes.length );
699
700 for ( String attribute:attributes )
701 {
702 EntryAttribute attr = get( attribute );
703
704 if ( attr != null )
705 {
706 removed.add( this.attributes.remove( attr.getId() ) );
707 }
708 else
709 {
710 String message = "The attribute '" + attribute + "' does not exist in the entry";
711 LOG.warn( message );
712 continue;
713 }
714 }
715
716 if ( removed.size() == 0 )
717 {
718 return null;
719 }
720 else
721 {
722 return removed;
723 }
724 }
725
726
727 /**
728 * <p>
729 * Removes the specified binary values from an attribute.
730 * </p>
731 * <p>
732 * If at least one value is removed, this method returns <code>true</code>.
733 * </p>
734 * <p>
735 * If there is no more value after having removed the values, the attribute
736 * will be removed too.
737 * </p>
738 * <p>
739 * If the attribute does not exist, nothing is done and the method returns
740 * <code>false</code>
741 * </p>
742 *
743 * @param upId The attribute ID
744 * @param values the values to be removed
745 * @return <code>true</code> if at least a value is removed, <code>false</code>
746 * if not all the values have been removed or if the attribute does not exist.
747 */
748 public boolean remove( String upId, byte[]... values ) throws NamingException
749 {
750 try
751 {
752 String id = getId( upId );
753
754 EntryAttribute attribute = get( id );
755
756 if ( attribute == null )
757 {
758 // Can't remove values from a not existing attribute !
759 return false;
760 }
761
762 int nbOldValues = attribute.size();
763
764 // Remove the values
765 attribute.remove( values );
766
767 if ( attribute.size() == 0 )
768 {
769 // No mare values, remove the attribute
770 attributes.remove( id );
771
772 return true;
773 }
774
775 if ( nbOldValues != attribute.size() )
776 {
777 // At least one value have been removed, return true.
778 return true;
779 }
780 else
781 {
782 // No values have been removed, return false.
783 return false;
784 }
785 }
786 catch ( IllegalArgumentException iae )
787 {
788 LOG.error( "The removal of values for the missing '{}' attribute is not possible", upId );
789 return false;
790 }
791 }
792
793
794 /**
795 * <p>
796 * Removes the specified String values from an attribute.
797 * </p>
798 * <p>
799 * If at least one value is removed, this method returns <code>true</code>.
800 * </p>
801 * <p>
802 * If there is no more value after having removed the values, the attribute
803 * will be removed too.
804 * </p>
805 * <p>
806 * If the attribute does not exist, nothing is done and the method returns
807 * <code>false</code>
808 * </p>
809 *
810 * @param upId The attribute ID
811 * @param attributes the attributes to be removed
812 * @return <code>true</code> if at least a value is removed, <code>false</code>
813 * if not all the values have been removed or if the attribute does not exist.
814 */
815 public boolean remove( String upId, String... values ) throws NamingException
816 {
817 try
818 {
819 String id = getId( upId );
820
821 EntryAttribute attribute = get( id );
822
823 if ( attribute == null )
824 {
825 // Can't remove values from a not existing attribute !
826 return false;
827 }
828
829 int nbOldValues = attribute.size();
830
831 // Remove the values
832 attribute.remove( values );
833
834 if ( attribute.size() == 0 )
835 {
836 // No mare values, remove the attribute
837 attributes.remove( id );
838
839 return true;
840 }
841
842 if ( nbOldValues != attribute.size() )
843 {
844 // At least one value have been removed, return true.
845 return true;
846 }
847 else
848 {
849 // No values have been removed, return false.
850 return false;
851 }
852 }
853 catch ( IllegalArgumentException iae )
854 {
855 LOG.error( "The removal of values for the missing '{}' attribute is not possible", upId );
856 return false;
857 }
858 }
859
860
861 /**
862 * <p>
863 * Removes the specified values from an attribute.
864 * </p>
865 * <p>
866 * If at least one value is removed, this method returns <code>true</code>.
867 * </p>
868 * <p>
869 * If there is no more value after having removed the values, the attribute
870 * will be removed too.
871 * </p>
872 * <p>
873 * If the attribute does not exist, nothing is done and the method returns
874 * <code>false</code>
875 * </p>
876 *
877 * @param upId The attribute ID
878 * @param attributes the attributes to be removed
879 * @return <code>true</code> if at least a value is removed, <code>false</code>
880 * if not all the values have been removed or if the attribute does not exist.
881 */
882 public boolean remove( String upId, Value<?>... values ) throws NamingException
883 {
884 try
885 {
886 String id = getId( upId );
887
888 EntryAttribute attribute = get( id );
889
890 if ( attribute == null )
891 {
892 // Can't remove values from a not existing attribute !
893 return false;
894 }
895
896 int nbOldValues = attribute.size();
897
898 // Remove the values
899 attribute.remove( values );
900
901 if ( attribute.size() == 0 )
902 {
903 // No mare values, remove the attribute
904 attributes.remove( id );
905
906 return true;
907 }
908
909 if ( nbOldValues != attribute.size() )
910 {
911 // At least one value have been removed, return true.
912 return true;
913 }
914 else
915 {
916 // No values have been removed, return false.
917 return false;
918 }
919 }
920 catch ( IllegalArgumentException iae )
921 {
922 LOG.error( "The removal of values for the missing '{}' attribute is not possible", upId );
923 return false;
924 }
925 }
926
927
928 public Iterator<EntryAttribute> iterator()
929 {
930 return Collections.unmodifiableMap( attributes ).values().iterator();
931 }
932
933
934 /**
935 * @see Externalizable#writeExternal(ObjectOutput)<p>
936 *
937 * This is the place where we serialize entries, and all theirs
938 * elements.
939 * <p>
940 * The structure used to store the entry is the following :
941 * <li>
942 * <b>[DN]</b> : If it's null, stores an empty DN
943 * </li>
944 * <li>
945 * <b>[attributes number]</b> : the number of attributes.
946 * </li>
947 * <li>
948 * <b>[attribute]*</b> : each attribute, if we have some
949 * </li>
950 */
951 public void writeExternal( ObjectOutput out ) throws IOException
952 {
953 // First, the DN
954 if ( dn == null )
955 {
956 // Write an empty DN
957 out.writeObject( LdapDN.EMPTY_LDAPDN );
958 }
959 else
960 {
961 // Write the DN
962 out.writeObject( dn );
963 }
964
965 // Then the attributes.
966 // Store the attributes' nulber first
967 out.writeInt( attributes.size() );
968
969 // Iterate through the keys.
970 for ( EntryAttribute attribute:attributes.values() )
971 {
972 // Store the attribute
973 out.writeObject( attribute );
974 }
975
976 out.flush();
977 }
978
979
980 /**
981 * @see Externalizable#readExternal(ObjectInput)
982 */
983 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
984 {
985 // Read the DN
986 dn = (LdapDN)in.readObject();
987
988 // Read the number of attributes
989 int nbAttributes = in.readInt();
990
991 // Read the attributes
992 for ( int i = 0; i < nbAttributes; i++ )
993 {
994 // Read each attribute
995 EntryAttribute attribute = (DefaultClientAttribute)in.readObject();
996
997 attributes.put( attribute.getId(), attribute );
998 }
999 }
1000
1001
1002 /**
1003 * Get the hash code of this ClientEntry.
1004 *
1005 * @see java.lang.Object#hashCode()
1006 * @return the instance's hash code
1007 */
1008 public int hashCode()
1009 {
1010 int result = 37;
1011
1012 result = result*17 + dn.hashCode();
1013
1014 SortedMap<String, EntryAttribute> sortedMap = new TreeMap<String, EntryAttribute>();
1015
1016 for ( String id:attributes.keySet() )
1017 {
1018 sortedMap.put( id, attributes.get( id ) );
1019 }
1020
1021 for ( String id:sortedMap.keySet() )
1022 {
1023 result = result*17 + sortedMap.get( id ).hashCode();
1024 }
1025
1026 return result;
1027 }
1028
1029
1030 /**
1031 * Tells if an entry has a specific ObjectClass value
1032 *
1033 * @param objectClass The ObjectClass we want to check
1034 * @return <code>true</code> if the ObjectClass value is present
1035 * in the ObjectClass attribute
1036 */
1037 public boolean hasObjectClass( String objectClass )
1038 {
1039 return contains( "objectclass", objectClass );
1040 }
1041
1042
1043 /**
1044 * @see Object#equals(Object)
1045 */
1046 public boolean equals( Object o )
1047 {
1048 // Short circuit
1049
1050 if ( this == o )
1051 {
1052 return true;
1053 }
1054
1055 if ( ! ( o instanceof DefaultClientEntry ) )
1056 {
1057 return false;
1058 }
1059
1060 DefaultClientEntry other = (DefaultClientEntry)o;
1061
1062 // Both DN must be equal
1063 if ( dn == null )
1064 {
1065 if ( other.getDn() != null )
1066 {
1067 return false;
1068 }
1069 }
1070 else
1071 {
1072 if ( !dn.equals( other.getDn() ) )
1073 {
1074 return false;
1075 }
1076 }
1077
1078 // They must have the same number of attributes
1079 if ( size() != other.size() )
1080 {
1081 return false;
1082 }
1083
1084 // Each attribute must be equal
1085 for ( EntryAttribute attribute:other )
1086 {
1087 if ( !attribute.equals( this.get( attribute.getId() ) ) )
1088 {
1089 return false;
1090 }
1091 }
1092
1093 return true;
1094 }
1095
1096
1097 /**
1098 * @see Object#toString()
1099 */
1100 public String toString()
1101 {
1102 StringBuilder sb = new StringBuilder();
1103
1104 sb.append( "ClientEntry\n" );
1105 sb.append( " dn: " ).append( dn.getUpName() ).append( '\n' );
1106
1107 // First dump the ObjectClass attribute
1108 if ( containsAttribute( "objectClass" ) )
1109 {
1110 EntryAttribute objectClass = get( "objectclass" );
1111
1112 sb.append( objectClass );
1113 }
1114
1115 if ( attributes.size() != 0 )
1116 {
1117 for ( EntryAttribute attribute:attributes.values() )
1118 {
1119 if ( !attribute.getId().equals( "objectclass" ) )
1120 {
1121 sb.append( attribute );
1122 }
1123 }
1124 }
1125
1126 return sb.toString();
1127 }
1128 }