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