001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.types;
028 import org.opends.messages.Message;
029 import org.opends.messages.MessageBuilder;
030
031
032 import java.io.BufferedWriter;
033 import java.io.IOException;
034 import java.util.ArrayList;
035 import java.util.Collection;
036 import java.util.HashMap;
037 import java.util.HashSet;
038 import java.util.Iterator;
039 import java.util.LinkedHashMap;
040 import java.util.LinkedHashSet;
041 import java.util.LinkedList;
042 import java.util.List;
043 import java.util.Map;
044 import java.util.Set;
045 import java.util.concurrent.locks.Lock;
046
047 import org.opends.server.api.AttributeValueDecoder;
048 import org.opends.server.api.CompressedSchema;
049 import org.opends.server.api.ProtocolElement;
050 import org.opends.server.api.plugin.PluginResult;
051 import org.opends.server.core.DirectoryServer;
052 import org.opends.server.core.PluginConfigManager;
053 import org.opends.server.protocols.asn1.ASN1Element;
054 import org.opends.server.protocols.asn1.ASN1OctetString;
055 import org.opends.server.util.LDIFException;
056
057 import static org.opends.server.config.ConfigConstants.*;
058 import static org.opends.server.loggers.debug.DebugLogger.*;
059 import org.opends.server.loggers.debug.DebugTracer;
060 import static org.opends.server.loggers.ErrorLogger.*;
061 import static org.opends.messages.CoreMessages.*;
062 import static org.opends.messages.UtilityMessages.*;
063 import static org.opends.server.util.LDIFWriter.*;
064 import static org.opends.server.util.ServerConstants.*;
065 import static org.opends.server.util.StaticUtils.*;
066
067
068
069 /**
070 * This class defines a data structure for a Directory Server entry.
071 * It includes a DN and a set of attributes.
072 * <BR><BR>
073 * The entry also contains a volatile attachment object, which should
074 * be used to associate the entry with a special type of object that
075 * is based on its contents. For example, if the entry holds access
076 * control information, then the attachment might be an object that
077 * contains a representation of that access control definition in a
078 * more useful form. This is only useful if the entry is to be
079 * cached, since the attachment may be accessed if the entry is
080 * retrieved from the cache, but if the entry is retrieved from the
081 * backend repository it cannot be guaranteed to contain any
082 * attachment (and in most cases will not). This attachment is
083 * volatile in that it is not always guaranteed to be present, it may
084 * be removed or overwritten at any time, and it will be invalidated
085 * and removed if the entry is altered in any way.
086 */
087 @org.opends.server.types.PublicAPI(
088 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
089 mayInstantiate=true,
090 mayExtend=false,
091 mayInvoke=true)
092 public class Entry
093 implements ProtocolElement
094 {
095 /**
096 * The tracer object for the debug logger.
097 */
098 private static final DebugTracer TRACER = getTracer();
099
100 // Indicates whether virtual attribute processing has been performed
101 // for this entry.
102 private boolean virtualAttributeProcessingPerformed;
103
104 // The set of operational attributes for this entry.
105 private Map<AttributeType,List<Attribute>> operationalAttributes;
106
107 // The set of user attributes for this entry.
108 private Map<AttributeType,List<Attribute>> userAttributes;
109
110 // The set of suppressed real attributes for this entry.
111 private Map<AttributeType,List<Attribute>> suppressedAttributes;
112
113 // The set of objectclasses for this entry.
114 private Map<ObjectClass,String> objectClasses;
115
116 // The DN for this entry.
117 private DN dn;
118
119 // A generic attachment that may be used to associate this entry
120 // with some other object.
121 private transient Object attachment;
122
123 // The schema used to govern this entry.
124 private Schema schema;
125
126
127
128 /**
129 * Creates a new entry with the provided information.
130 *
131 * @param dn The distinguished name for this
132 * entry.
133 * @param objectClasses The set of objectclasses for this
134 * entry as a mapping between the
135 * objectclass and the name to use to
136 * reference it.
137 * @param userAttributes The set of user attributes for
138 * this entry as a mapping between
139 * the attribute type and the list of
140 * attributes with that type.
141 * @param operationalAttributes The set of operational attributes
142 * for this entry as a mapping
143 * between the attribute type and the
144 * list of attributes with that type.
145 */
146 public Entry(DN dn, Map<ObjectClass,String> objectClasses,
147 Map<AttributeType,List<Attribute>> userAttributes,
148 Map<AttributeType,List<Attribute>>
149 operationalAttributes)
150 {
151 attachment = null;
152 schema = DirectoryServer.getSchema();
153 virtualAttributeProcessingPerformed = false;
154
155
156 suppressedAttributes =
157 new LinkedHashMap<AttributeType,List<Attribute>>();
158
159
160 if (dn == null)
161 {
162 this.dn = DN.nullDN();
163 }
164 else
165 {
166 this.dn = dn;
167 }
168
169 if (objectClasses == null)
170 {
171 this.objectClasses = new HashMap<ObjectClass,String>();
172 }
173 else
174 {
175 this.objectClasses = objectClasses;
176 }
177
178 if (userAttributes == null)
179 {
180 this.userAttributes =
181 new HashMap<AttributeType,List<Attribute>>();
182 }
183 else
184 {
185 this.userAttributes = userAttributes;
186 }
187
188 if (operationalAttributes == null)
189 {
190 this.operationalAttributes =
191 new HashMap<AttributeType,List<Attribute>>();
192 }
193 else
194 {
195 this.operationalAttributes = operationalAttributes;
196 }
197 }
198
199
200
201 /**
202 * Retrieves the distinguished name for this entry.
203 *
204 * @return The distinguished name for this entry.
205 */
206 public DN getDN()
207 {
208 return dn;
209 }
210
211
212
213 /**
214 * Specifies the distinguished name for this entry.
215 *
216 * @param dn The distinguished name for this entry.
217 */
218 public void setDN(DN dn)
219 {
220 if (dn == null)
221 {
222 this.dn = DN.nullDN();
223 }
224 else
225 {
226 this.dn = dn;
227 }
228
229 attachment = null;
230 }
231
232
233
234 /**
235 * Retrieves the set of objectclasses defined for this entry. The
236 * caller should be allowed to modify the contents of this list, but
237 * if it does then it should also invalidate the attachment.
238 *
239 * @return The set of objectclasses defined for this entry.
240 */
241 public Map<ObjectClass,String> getObjectClasses()
242 {
243 return objectClasses;
244 }
245
246
247
248 /**
249 * Indicates whether this entry has the specified objectclass.
250 *
251 * @param objectClass The objectclass for which to make the
252 * determination.
253 *
254 * @return <CODE>true</CODE> if this entry has the specified
255 * objectclass, or <CODE>false</CODE> if not.
256 */
257 public boolean hasObjectClass(ObjectClass objectClass)
258 {
259 return objectClasses.containsKey(objectClass);
260 }
261
262
263
264 /**
265 * Retrieves the structural objectclass for this entry.
266 *
267 * @return The structural objectclass for this entry, or
268 * <CODE>null</CODE> if there is none for some reason. If
269 * there are multiple structural classes in the entry, then
270 * the first will be returned.
271 */
272 public ObjectClass getStructuralObjectClass()
273 {
274 ObjectClass structuralClass = null;
275
276 for (ObjectClass oc : objectClasses.keySet())
277 {
278 if (oc.getObjectClassType() == ObjectClassType.STRUCTURAL)
279 {
280 if (structuralClass == null)
281 {
282 structuralClass = oc;
283 }
284 else
285 {
286 if (oc.isDescendantOf(structuralClass))
287 {
288 structuralClass = oc;
289 }
290 }
291 }
292 }
293
294 return structuralClass;
295 }
296
297
298
299 /**
300 * Specifies the set of objectclasses for this entry.
301 *
302 * @param objectClassNames The values containing the names or OIDs
303 * of the objectClasses for this entry.
304 *
305 * @throws DirectoryException If a problem occurs while attempting
306 * to set the objectclasses for this
307 * entry.
308 */
309 public void setObjectClasses(
310 Collection<AttributeValue> objectClassNames)
311 throws DirectoryException
312 {
313 attachment = null;
314
315 // Iterate through all the provided objectclass names and make
316 // sure that they are names of valid objectclasses.
317 LinkedHashMap<ObjectClass,String> ocMap =
318 new LinkedHashMap<ObjectClass,String>();
319 for (AttributeValue v : objectClassNames)
320 {
321 String name = v.getStringValue();
322
323 String lowerName;
324 try
325 {
326 lowerName = v.getNormalizedStringValue();
327 }
328 catch (Exception e)
329 {
330 if (debugEnabled())
331 {
332 TRACER.debugCaught(DebugLogLevel.ERROR, e);
333 }
334
335 lowerName = toLowerCase(v.getStringValue());
336 }
337
338 ObjectClass oc = DirectoryServer.getObjectClass(lowerName);
339 if (oc == null)
340 {
341 Message message =
342 ERR_ENTRY_ADD_UNKNOWN_OC.get(name, String.valueOf(dn));
343 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION,
344 message);
345 }
346
347 ocMap.put(oc, name);
348 }
349
350
351 // If we've gotten here, then everything is fine so put the new
352 // set of objectclasses.
353 objectClasses = ocMap;
354 }
355
356
357
358 /**
359 * Adds the objectClass with the given name to this entry.
360 *
361 * @param objectClassName The value containing the name or OID of
362 * the objectClass to add to this entry.
363 *
364 * @throws DirectoryException If a problem occurs while attempting
365 * to add the objectclass to this
366 * entry.
367 */
368 public void addObjectClass(AttributeValue objectClassName)
369 throws DirectoryException
370 {
371 attachment = null;
372
373 String name = objectClassName.getStringValue();
374
375 String lowerName;
376 try
377 {
378 lowerName = objectClassName.getNormalizedStringValue();
379 }
380 catch (Exception e)
381 {
382 if (debugEnabled())
383 {
384 TRACER.debugCaught(DebugLogLevel.ERROR, e);
385 }
386
387 lowerName = toLowerCase(name);
388 }
389
390 ObjectClass oc = DirectoryServer.getObjectClass(lowerName, true);
391 if (objectClasses.containsKey(oc))
392 {
393 Message message =
394 ERR_ENTRY_ADD_DUPLICATE_OC.get(name, String.valueOf(dn));
395 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION,
396 message);
397 }
398
399 objectClasses.put(oc, name);
400 }
401
402
403
404 /**
405 * Adds the provided objectClass to this entry.
406 *
407 * @param oc The objectClass to add to this entry.
408 *
409 * @throws DirectoryException If a problem occurs while attempting
410 * to add the objectclass to this
411 * entry.
412 */
413 public void addObjectClass(ObjectClass oc)
414 throws DirectoryException
415 {
416 attachment = null;
417
418 if (objectClasses.containsKey(oc))
419 {
420 Message message = ERR_ENTRY_ADD_DUPLICATE_OC.get(
421 oc.getNameOrOID(), String.valueOf(dn));
422 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION,
423 message);
424 }
425
426 objectClasses.put(oc, oc.getNameOrOID());
427 }
428
429
430
431 /**
432 * Adds the objectclasses corresponding to the provided set of names
433 * to this entry.
434 *
435 * @param objectClassNames The values containing the names or OIDs
436 * of the objectClasses to add to this
437 * entry.
438 *
439 * @throws DirectoryException If a problem occurs while attempting
440 * to add the set of objectclasses to
441 * this entry.
442 */
443 public void addObjectClasses(
444 Collection<AttributeValue> objectClassNames)
445 throws DirectoryException
446 {
447 attachment = null;
448
449
450 // Iterate through all the provided objectclass names and make
451 // sure that they are names of valid objectclasses not already
452 // assigned to the entry.
453 LinkedHashMap<ObjectClass,String> tmpOCMap =
454 new LinkedHashMap<ObjectClass,String>();
455 for (AttributeValue v : objectClassNames)
456 {
457 String name = v.getStringValue();
458
459 String lowerName;
460 try
461 {
462 lowerName = v.getNormalizedStringValue();
463 }
464 catch (Exception e)
465 {
466 if (debugEnabled())
467 {
468 TRACER.debugCaught(DebugLogLevel.ERROR, e);
469 }
470
471 lowerName = toLowerCase(v.getStringValue());
472 }
473
474 ObjectClass oc = DirectoryServer.getObjectClass(lowerName);
475 if (oc == null)
476 {
477 Message message =
478 ERR_ENTRY_ADD_UNKNOWN_OC.get(name, String.valueOf(dn));
479 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION,
480 message);
481 }
482
483 if (objectClasses.containsKey(oc))
484 {
485 Message message =
486 ERR_ENTRY_ADD_DUPLICATE_OC.get(name, String.valueOf(dn));
487 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION,
488 message);
489 }
490
491 if (oc.isObsolete())
492 {
493 Message message =
494 ERR_ENTRY_ADD_OBSOLETE_OC.get(name, String.valueOf(dn));
495 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION,
496 message);
497 }
498
499 tmpOCMap.put(oc, name);
500 }
501
502
503 // If we've gotten here, then everything is OK, so add the new
504 // classes.
505 for (ObjectClass oc : tmpOCMap.keySet())
506 {
507 String name = tmpOCMap.get(oc);
508
509 objectClasses.put(oc, name);
510 }
511 }
512
513
514
515 /**
516 * Retrieves the entire set of attributes for this entry. This will
517 * include both user and operational attributes. The caller must
518 * not modify the contents of this list. Also note that this method
519 * is less efficient than calling either (or both)
520 * <CODE>getUserAttributes</CODE> or
521 * <CODE>getOperationalAttributes</CODE>, so it should only be used
522 * when calls to those methods are not appropriate.
523 *
524 * @return The entire set of attributes for this entry.
525 */
526 public List<Attribute> getAttributes()
527 {
528 ArrayList<Attribute> attributes = new ArrayList<Attribute>();
529
530 for (List<Attribute> list : userAttributes.values())
531 {
532 for (Attribute a : list)
533 {
534 attributes.add(a);
535 }
536 }
537
538 for (List<Attribute> list : operationalAttributes.values())
539 {
540 for (Attribute a : list)
541 {
542 attributes.add(a);
543 }
544 }
545
546 return attributes;
547 }
548
549
550
551 /**
552 * Retrieves the entire set of user (i.e., non-operational)
553 * attributes for this entry. The caller should be allowed to
554 * modify the contents of this list, but if it does then it should
555 * also invalidate the attachment.
556 *
557 * @return The entire set of user attributes for this entry.
558 */
559 public Map<AttributeType,List<Attribute>> getUserAttributes()
560 {
561 return userAttributes;
562 }
563
564
565
566 /**
567 * Retrieves the entire set of operational attributes for this
568 * entry. The caller should be allowed to modify the contents of
569 * this list, but if it does then it should also invalidate the
570 * attachment.
571 *
572 * @return The entire set of operational attributes for this entry.
573 */
574 public Map<AttributeType,List<Attribute>> getOperationalAttributes()
575 {
576 return operationalAttributes;
577 }
578
579
580
581 /**
582 * Retrieves an attribute holding the objectclass information for
583 * this entry. The returned attribute must not be altered.
584 *
585 * @return An attribute holding the objectclass information for
586 * this entry, or <CODE>null</CODE> if it does not have any
587 * objectclass information.
588 */
589 public Attribute getObjectClassAttribute()
590 {
591 if ((objectClasses == null) || objectClasses.isEmpty())
592 {
593 return null;
594 }
595
596 AttributeType ocType =
597 DirectoryServer.getObjectClassAttributeType();
598
599 LinkedHashSet<AttributeValue> ocValues =
600 new LinkedHashSet<AttributeValue>(objectClasses.size());
601 for (String s : objectClasses.values())
602 {
603 ocValues.add(new AttributeValue(ocType,
604 new ASN1OctetString(s)));
605 }
606
607 return new Attribute(ocType, ATTR_OBJECTCLASS, ocValues);
608 }
609
610
611 /**
612 * Indicates whether this entry contains the specified attribute.
613 * Any subordinate attribute of the specified attribute will also
614 * be used in the determination.
615 *
616 *
617 * @param attributeType The attribute type for which to
618 * make the determination.
619 *
620 * @return <CODE>true</CODE> if this entry contains the specified
621 * attribute, or <CODE>false</CODE> if not.
622 */
623 public boolean hasAttribute(AttributeType attributeType)
624 {
625 return hasAttribute(attributeType, true);
626 }
627
628
629 /**
630 * Indicates whether this entry contains the specified attribute.
631 *
632 * @param attributeType The attribute type for which to
633 * make the determination.
634 * @param includeSubordinates Whether to include any subordinate
635 * attributes of the attribute type
636 * being retrieved.
637 *
638 * @return <CODE>true</CODE> if this entry contains the specified
639 * attribute, or <CODE>false</CODE> if not.
640 */
641 public boolean hasAttribute(AttributeType attributeType,
642 boolean includeSubordinates)
643 {
644 if (userAttributes.containsKey(attributeType) ||
645 operationalAttributes.containsKey(attributeType))
646 {
647 return true;
648 }
649
650 if (includeSubordinates &&
651 attributeType.mayHaveSubordinateTypes())
652 {
653 for (AttributeType at : schema.getSubTypes(attributeType))
654 {
655 if (userAttributes.containsKey(at) ||
656 operationalAttributes.containsKey(at))
657 {
658 return true;
659 }
660 }
661 }
662
663 return (attributeType.isObjectClassType() &&
664 (! objectClasses.isEmpty()));
665 }
666
667
668 /**
669 * Indicates whether this entry contains the specified attribute
670 * with all of the options in the provided set. Any subordinate
671 * attribute of the specified attribute will also be used in
672 * the determination.
673 *
674 * @param attributeType The attribute type for which to
675 * make the determination.
676 * @param attributeOptions The set of options to use in the
677 * determination.
678 *
679 * @return <CODE>true</CODE> if this entry contains the specified
680 * attribute, or <CODE>false</CODE> if not.
681 */
682 public boolean hasAttribute(AttributeType attributeType,
683 Set<String> attributeOptions)
684 {
685 return hasAttribute(attributeType, true, attributeOptions);
686 }
687
688
689 /**
690 * Indicates whether this entry contains the specified attribute
691 * with all of the options in the provided set.
692 *
693 * @param attributeType The attribute type for which to
694 * make the determination.
695 * @param includeSubordinates Whether to include any subordinate
696 * attributes of the attribute type
697 * being retrieved.
698 * @param attributeOptions The set of options to use in the
699 * determination.
700 *
701 * @return <CODE>true</CODE> if this entry contains the specified
702 * attribute, or <CODE>false</CODE> if not.
703 */
704 public boolean hasAttribute(AttributeType attributeType,
705 boolean includeSubordinates,
706 Set<String> attributeOptions)
707 {
708 List<Attribute> attributes;
709 if (includeSubordinates &&
710 attributeType.mayHaveSubordinateTypes())
711 {
712 attributes = new LinkedList<Attribute>();
713 List<Attribute> attrs = userAttributes.get(attributeType);
714 if (attrs != null)
715 {
716 attributes.addAll(attrs);
717 }
718
719 attrs = operationalAttributes.get(attributeType);
720 if (attrs != null)
721 {
722 attributes.addAll(attrs);
723 }
724
725 for (AttributeType at : schema.getSubTypes(attributeType))
726 {
727 attrs = userAttributes.get(at);
728 if (attrs != null)
729 {
730 attributes.addAll(attrs);
731 }
732
733 attrs = operationalAttributes.get(at);
734 if (attrs != null)
735 {
736 attributes.addAll(attrs);
737 }
738 }
739 }
740 else
741 {
742 attributes = userAttributes.get(attributeType);
743 if (attributes == null)
744 {
745 attributes = operationalAttributes.get(attributeType);
746 if (attributes == null)
747 {
748 if (attributeType.isObjectClassType() &&
749 (! objectClasses.isEmpty()))
750 {
751 return ((attributeOptions == null) ||
752 attributeOptions.isEmpty());
753 }
754 else
755 {
756 return false;
757 }
758 }
759 }
760 }
761
762 // It's possible that there could be an attribute without any
763 // values, which we should treat as not having the requested
764 // attribute.
765 for (Attribute a : attributes)
766 {
767 if (a.hasValue() && a.hasOptions(attributeOptions))
768 {
769 return true;
770 }
771 }
772
773 return false;
774 }
775
776 /**
777 * Retrieves the requested attribute element(s) for the specified
778 * attribute type. The list returned may include multiple elements
779 * if the same attribute exists in the entry multiple times with
780 * different sets of options. It may also include any subordinate
781 * attributes of the attribute being retrieved.
782 *
783 * @param attributeType The attribute type to retrieve.
784 *
785 * @return The requested attribute element(s) for the specified
786 * attribute type, or <CODE>null</CODE> if the specified
787 * attribute type is not present in this entry.
788 */
789 public List<Attribute> getAttribute(AttributeType attributeType)
790 {
791 return getAttribute(attributeType, true);
792 }
793
794
795 /**
796 * Retrieves the requested attribute element(s) for the specified
797 * attribute type. The list returned may include multiple elements
798 * if the same attribute exists in the entry multiple times with
799 * different sets of options.
800 *
801 * @param attributeType The attribute type to retrieve.
802 * @param includeSubordinates Whether to include any subordinate
803 * attributes of the attribute type
804 * being retrieved.
805 *
806 * @return The requested attribute element(s) for the specified
807 * attribute type, or <CODE>null</CODE> if the specified
808 * attribute type is not present in this entry.
809 */
810 public List<Attribute> getAttribute(AttributeType attributeType,
811 boolean includeSubordinates)
812 {
813 if (includeSubordinates &&
814 attributeType.mayHaveSubordinateTypes())
815 {
816 List<Attribute> attributes = new LinkedList<Attribute>();
817
818 List<Attribute> attrs = userAttributes.get(attributeType);
819 if (attrs != null)
820 {
821 attributes.addAll(attrs);
822 }
823
824 attrs = operationalAttributes.get(attributeType);
825 if (attrs != null)
826 {
827 attributes.addAll(attrs);
828 }
829
830 for (AttributeType at : schema.getSubTypes(attributeType))
831 {
832 attrs = userAttributes.get(at);
833 if (attrs != null)
834 {
835 attributes.addAll(attrs);
836 }
837
838 attrs = operationalAttributes.get(at);
839 if (attrs != null)
840 {
841 attributes.addAll(attrs);
842 }
843 }
844
845 if (attributes.isEmpty())
846 {
847 return null;
848 }
849 else
850 {
851 return attributes;
852 }
853 }
854 else
855 {
856 List<Attribute> attributes = userAttributes.get(attributeType);
857
858 if (attributes == null)
859 {
860 attributes = operationalAttributes.get(attributeType);
861 if (attributes == null)
862 {
863 if (attributeType.isObjectClassType() &&
864 (! objectClasses.isEmpty()))
865 {
866 attributes = new ArrayList<Attribute>(1);
867 attributes.add(getObjectClassAttribute());
868 return attributes;
869 }
870 else
871 {
872 return null;
873 }
874 }
875 else
876 {
877 return attributes;
878 }
879 }
880 else
881 {
882 return attributes;
883 }
884 }
885 }
886
887
888
889 /**
890 * Retrieves the requested attribute element(s) for the attribute
891 * with the specified name or OID. The list returned may include
892 * multiple elements if the same attribute exists in the entry
893 * multiple times with different sets of options. It may also
894 * include any subordinate attributes of the attribute being
895 * retrieved.
896 * <BR><BR>
897 * Note that this method should only be used in cases in which the
898 * Directory Server schema has no reference of an attribute type
899 * with the specified name. It is not as accurate or efficient as
900 * the version of this method that takes an
901 * <CODE>AttributeType</CODE> argument.
902 *
903 * @param lowerName The name or OID of the attribute to return,
904 * formatted in all lowercase characters.
905 *
906 * @return The requested attribute element(s) for the specified
907 * attribute type, or <CODE>null</CODE> if the specified
908 * attribute type is not present in this entry.
909 */
910 public List<Attribute> getAttribute(String lowerName)
911 {
912 for (AttributeType attr : userAttributes.keySet())
913 {
914 if (attr.hasNameOrOID(lowerName))
915 {
916 return getAttribute(attr, true);
917 }
918 }
919
920 for (AttributeType attr : operationalAttributes.keySet())
921 {
922 if (attr.hasNameOrOID(lowerName))
923 {
924 return getAttribute(attr, true);
925 }
926 }
927
928 if (lowerName.equals(OBJECTCLASS_ATTRIBUTE_TYPE_NAME) &&
929 (! objectClasses.isEmpty()))
930 {
931 LinkedList<Attribute> attrList = new LinkedList<Attribute>();
932 attrList.add(getObjectClassAttribute());
933 return attrList;
934 }
935
936 return null;
937 }
938
939 /**
940 * Retrieves the requested attribute element(s) for the specified
941 * attribute type. The list returned may include multiple elements
942 * if the same attribute exists in the entry multiple times with
943 * different sets of options. It may also include any subordinate
944 * attributes of the attribute being retrieved.
945 *
946 * @param attributeType The attribute type to retrieve.
947 * @param options The set of attribute options to
948 * include in matching elements.
949 *
950 * @return The requested attribute element(s) for the specified
951 * attribute type, or <CODE>null</CODE> if the specified
952 * attribute type is not present in this entry with the
953 * provided set of options.
954 */
955 public List<Attribute> getAttribute(AttributeType attributeType,
956 Set<String> options)
957 {
958 return getAttribute(attributeType, true, options);
959 }
960
961 /**
962 * Retrieves the requested attribute element(s) for the specified
963 * attribute type. The list returned may include multiple elements
964 * if the same attribute exists in the entry multiple times with
965 * different sets of options.
966 *
967 * @param attributeType The attribute type to retrieve.
968 * @param includeSubordinates Whether to include any subordinate
969 * attributes of the attribute type
970 * being retrieved.
971 * @param options The set of attribute options to
972 * include in matching elements.
973 *
974 * @return The requested attribute element(s) for the specified
975 * attribute type, or <CODE>null</CODE> if the specified
976 * attribute type is not present in this entry with the
977 * provided set of options.
978 */
979 public List<Attribute> getAttribute(AttributeType attributeType,
980 boolean includeSubordinates,
981 Set<String> options)
982 {
983 List<Attribute> attributes = new LinkedList<Attribute>();
984 if (includeSubordinates &&
985 attributeType.mayHaveSubordinateTypes())
986 {
987 List<Attribute> attrs = userAttributes.get(attributeType);
988 if (attrs != null)
989 {
990 attributes.addAll(attrs);
991 }
992
993 attrs = operationalAttributes.get(attributeType);
994 if (attrs != null)
995 {
996 attributes.addAll(attrs);
997 }
998
999 for (AttributeType at : schema.getSubTypes(attributeType))
1000 {
1001 attrs = userAttributes.get(at);
1002 if (attrs != null)
1003 {
1004 attributes.addAll(attrs);
1005 }
1006
1007 attrs = operationalAttributes.get(at);
1008 if (attrs != null)
1009 {
1010 attributes.addAll(attrs);
1011 }
1012 }
1013 }
1014 else
1015 {
1016 List<Attribute> attrs = userAttributes.get(attributeType);
1017 if (attrs == null)
1018 {
1019 attrs = operationalAttributes.get(attributeType);
1020 if (attrs == null)
1021 {
1022 if (attributeType.isObjectClassType() &&
1023 (! objectClasses.isEmpty()) &&
1024 ((options == null) || options.isEmpty()))
1025 {
1026 attributes.add(getObjectClassAttribute());
1027 return attributes;
1028 }
1029 else
1030 {
1031 return null;
1032 }
1033 }
1034 else
1035 {
1036 attributes.addAll(attrs);
1037 }
1038 }
1039 else
1040 {
1041 attributes.addAll(attrs);
1042 }
1043 }
1044
1045
1046 Iterator<Attribute> iterator = attributes.iterator();
1047 while (iterator.hasNext())
1048 {
1049 Attribute a = iterator.next();
1050 if (! a.hasOptions(options))
1051 {
1052 iterator.remove();
1053 }
1054 }
1055
1056 if (attributes.isEmpty())
1057 {
1058 return null;
1059 }
1060 else
1061 {
1062 return attributes;
1063 }
1064 }
1065
1066
1067
1068 /**
1069 * Retrieves the requested attribute element(s) for the attribute
1070 * with the specified name or OID and set of options. The list
1071 * returned may include multiple elements if the same attribute
1072 * exists in the entry multiple times with different sets of
1073 * matching options.
1074 * <BR><BR>
1075 * Note that this method should only be used in cases in which the
1076 * Directory Server schema has no reference of an attribute type
1077 * with the specified name. It is not as accurate or efficient as
1078 * the version of this method that takes an
1079 * <CODE>AttributeType</CODE> argument.
1080 *
1081 * @param lowerName The name or OID of the attribute to return,
1082 * formatted in all lowercase characters.
1083 * @param options The set of attribute options to include in
1084 * matching elements.
1085 *
1086 * @return The requested attribute element(s) for the specified
1087 * attribute type, or <CODE>null</CODE> if the specified
1088 * attribute type is not present in this entry.
1089 */
1090 public List<Attribute> getAttribute(String lowerName,
1091 Set<String> options)
1092 {
1093 for (AttributeType attr : userAttributes.keySet())
1094 {
1095 if (attr.hasNameOrOID(lowerName))
1096 {
1097 return getAttribute(attr, options);
1098 }
1099 }
1100
1101 for (AttributeType attr : operationalAttributes.keySet())
1102 {
1103 if (attr.hasNameOrOID(lowerName))
1104 {
1105 return getAttribute(attr, options);
1106 }
1107 }
1108
1109 if (lowerName.equals(OBJECTCLASS_ATTRIBUTE_TYPE_NAME) &&
1110 ((options == null) || options.isEmpty()))
1111 {
1112 LinkedList<Attribute> attributes = new LinkedList<Attribute>();
1113 attributes.add(getObjectClassAttribute());
1114 return attributes;
1115 }
1116 else
1117 {
1118 return null;
1119 }
1120 }
1121
1122
1123
1124 /**
1125 * Retrieves the requested attribute type from the entry and decodes
1126 * a single value as an object of type T.
1127 * <p>
1128 * If the requested attribute type is not present then
1129 * <code>null</code> is returned. If more than one attribute value
1130 * is present, then the first value found will be decoded and
1131 * returned.
1132 * <p>
1133 * The attribute value is decoded using the specified
1134 * {@link org.opends.server.api.AttributeValueDecoder}.
1135 *
1136 * @param <T>
1137 * Decode the attribute value to an object of this type.
1138 * @param attributeType
1139 * The attribute type to retrieve.
1140 * @param decoder
1141 * The attribute value decoder.
1142 * @return The decoded attribute value or <code>null</code> if no
1143 * attribute value having the specified attribute type was
1144 * found.
1145 * @throws DirectoryException
1146 * If the requested attribute value could not be decoded
1147 * successfully.
1148 */
1149 public final <T> T getAttributeValue(AttributeType attributeType,
1150 AttributeValueDecoder<T> decoder) throws DirectoryException
1151 {
1152 List<Attribute> attributes = getAttribute(attributeType, true);
1153 AttributeValueIterable values =
1154 new AttributeValueIterable(attributes);
1155 Iterator<AttributeValue> iterator = values.iterator();
1156
1157 if (iterator.hasNext())
1158 {
1159 return decoder.decode(iterator.next());
1160 }
1161 else
1162 {
1163 return null;
1164 }
1165 }
1166
1167
1168
1169 /**
1170 * Retrieves the requested attribute type from the entry and decodes
1171 * any values as objects of type T and then places them in the
1172 * specified collection.
1173 * <p>
1174 * If the requested attribute type is not present then no decoded
1175 * values will be added to the container.
1176 * <p>
1177 * The attribute value is decoded using the specified
1178 * {@link org.opends.server.api.AttributeValueDecoder}.
1179 *
1180 * @param <T>
1181 * Decode the attribute values to objects of this type.
1182 * @param attributeType
1183 * The attribute type to retrieve.
1184 * @param decoder
1185 * The attribute value decoder.
1186 * @param collection
1187 * The collection to which decoded values should be added.
1188 * @return The collection containing the decoded attribute value.
1189 * @throws DirectoryException
1190 * If one or more of the requested attribute values could
1191 * not be decoded successfully.
1192 */
1193 public final <T> Collection<T> getAttributeValues(
1194 AttributeType attributeType,
1195 AttributeValueDecoder<? extends T> decoder,
1196 Collection<T> collection)
1197 throws DirectoryException
1198 {
1199 List<Attribute> attributes = getAttribute(attributeType, true);
1200 AttributeValueIterable values =
1201 new AttributeValueIterable(attributes);
1202
1203 for (AttributeValue value : values)
1204 {
1205 collection.add(decoder.decode(value));
1206 }
1207
1208 return collection;
1209 }
1210
1211
1212
1213 /**
1214 * Indicates whether this entry contains the specified user
1215 * attribute.
1216 *
1217 * @param attributeType
1218 * The attribute type for which to make the determination.
1219 * @return <CODE>true</CODE> if this entry contains the specified
1220 * user attribute, or <CODE>false</CODE> if not.
1221 */
1222 public boolean hasUserAttribute(AttributeType attributeType)
1223 {
1224 if (userAttributes.containsKey(attributeType))
1225 {
1226 return true;
1227 }
1228
1229 if (attributeType.mayHaveSubordinateTypes())
1230 {
1231 for (AttributeType at : schema.getSubTypes(attributeType))
1232 {
1233 if (userAttributes.containsKey(at))
1234 {
1235 return true;
1236 }
1237 }
1238 }
1239
1240 return false;
1241 }
1242
1243
1244
1245 /**
1246 * Retrieves the requested user attribute element(s) for the
1247 * specified attribute type. The list returned may include multiple
1248 * elements if the same attribute exists in the entry multiple times
1249 * with different sets of options.
1250 *
1251 * @param attributeType The attribute type to retrieve.
1252 *
1253 * @return The requested attribute element(s) for the specified
1254 * attribute type, or <CODE>null</CODE> if there is no such
1255 * user attribute.
1256 */
1257 public List<Attribute> getUserAttribute(AttributeType attributeType)
1258 {
1259 if (attributeType.mayHaveSubordinateTypes())
1260 {
1261 LinkedList<Attribute> attributes = new LinkedList<Attribute>();
1262
1263 List<Attribute> attrs = userAttributes.get(attributeType);
1264 if (attrs != null)
1265 {
1266 attributes.addAll(attrs);
1267 }
1268
1269 for (AttributeType at : schema.getSubTypes(attributeType))
1270 {
1271 attrs = userAttributes.get(at);
1272 if (attrs != null)
1273 {
1274 attributes.addAll(attrs);
1275 }
1276 }
1277
1278 if (attributes.isEmpty())
1279 {
1280 return null;
1281 }
1282 else
1283 {
1284 return attributes;
1285 }
1286 }
1287 else
1288 {
1289 return userAttributes.get(attributeType);
1290 }
1291 }
1292
1293
1294
1295 /**
1296 * Retrieves the requested user attribute element(s) for the
1297 * specified attribute type. The list returned may include multiple
1298 * elements if the same attribute exists in the entry multiple times
1299 * with different sets of options.
1300 *
1301 * @param attributeType The attribute type to retrieve.
1302 * @param options The set of attribute options to include in
1303 * matching elements.
1304 *
1305 * @return The requested attribute element(s) for the specified
1306 * attribute type, or <CODE>null</CODE> if there is no such
1307 * user attribute with the specified set of options.
1308 */
1309 public List<Attribute> getUserAttribute(AttributeType attributeType,
1310 Set<String> options)
1311 {
1312 LinkedList<Attribute> attributes = new LinkedList<Attribute>();
1313 List<Attribute> attrs = userAttributes.get(attributeType);
1314 if (attrs != null)
1315 {
1316 attributes.addAll(attrs);
1317 }
1318
1319 if (attributeType.mayHaveSubordinateTypes())
1320 {
1321 for (AttributeType at : schema.getSubTypes(attributeType))
1322 {
1323 attrs = userAttributes.get(at);
1324 if (attrs != null)
1325 {
1326 attributes.addAll(attrs);
1327 }
1328 }
1329 }
1330
1331 Iterator<Attribute> iterator = attributes.iterator();
1332 while (iterator.hasNext())
1333 {
1334 Attribute a = iterator.next();
1335 if (! a.hasOptions(options))
1336 {
1337 iterator.remove();
1338 }
1339 }
1340
1341 if (attributes.isEmpty())
1342 {
1343 return null;
1344 }
1345 else
1346 {
1347 return attributes;
1348 }
1349 }
1350
1351
1352
1353 /**
1354 * Retrieves a duplicate of the user attribute list for the
1355 * specified type.
1356 *
1357 * @param attributeType The attribute type for which to retrieve a
1358 * duplicate attribute list.
1359 *
1360 * @return A duplicate of the requested attribute list, or
1361 * <CODE>null</CODE> if there is no such user attribute.
1362 */
1363 public List<Attribute> duplicateUserAttribute(
1364 AttributeType attributeType)
1365 {
1366 LinkedList<Attribute> attributes = new LinkedList<Attribute>();
1367
1368 List<Attribute> attrs = userAttributes.get(attributeType);
1369 if (attrs != null)
1370 {
1371 for (Attribute a : attrs)
1372 {
1373 attributes.add(a.duplicate());
1374 }
1375 }
1376
1377 if (attributeType.mayHaveSubordinateTypes())
1378 {
1379 for (AttributeType at : schema.getSubTypes(attributeType))
1380 {
1381 attrs = userAttributes.get(at);
1382 if (attrs != null)
1383 {
1384 for (Attribute a : attrs)
1385 {
1386 attributes.add(a.duplicate());
1387 }
1388 }
1389 }
1390 }
1391
1392 if (attributes.isEmpty())
1393 {
1394 return null;
1395 }
1396 else
1397 {
1398 return attributes;
1399 }
1400 }
1401
1402
1403
1404 /**
1405 * Makes a copy of attributes matching the specified options.
1406 *
1407 * @param attrList The attributes to be copied.
1408 * @param options The set of attribute options to include in
1409 * matching elements.
1410 * @param omitValues <CODE>true</CODE> if the values are to be
1411 * omitted.
1412 *
1413 * @return A copy of the attributes matching the specified options,
1414 * or <CODE>null</CODE> if there is no such attribute with
1415 * the specified set of options.
1416 */
1417 private static List<Attribute> duplicateAttribute(
1418 List<Attribute> attrList,
1419 Set<String> options,
1420 boolean omitValues)
1421 {
1422 if (attrList == null)
1423 {
1424 return null;
1425 }
1426
1427 ArrayList<Attribute> duplicateList =
1428 new ArrayList<Attribute>(attrList.size());
1429 for (Attribute a : attrList)
1430 {
1431 if (a.hasOptions(options))
1432 {
1433 duplicateList.add(a.duplicate(omitValues));
1434 }
1435 }
1436
1437 if (duplicateList.isEmpty())
1438 {
1439 return null;
1440 }
1441 else
1442 {
1443 return duplicateList;
1444 }
1445 }
1446
1447
1448
1449 /**
1450 * Retrieves a copy of the requested user attribute element(s) for
1451 * the specified attribute type. The list returned may include
1452 * multiple elements if the same attribute exists in the entry
1453 * multiple times with different sets of options.
1454 *
1455 * @param attributeType The attribute type to retrieve.
1456 * @param options The set of attribute options to include in
1457 * matching elements.
1458 * @param omitValues <CODE>true</CODE> if the values are to be
1459 * omitted.
1460 *
1461 * @return A copy of the requested attribute element(s) for the
1462 * specified attribute type, or <CODE>null</CODE> if there
1463 * is no such user attribute with the specified set of
1464 * options.
1465 */
1466 public List<Attribute> duplicateUserAttribute(
1467 AttributeType attributeType,
1468 Set<String> options,
1469 boolean omitValues)
1470 {
1471 List<Attribute> currentList = getUserAttribute(attributeType);
1472 return duplicateAttribute(currentList, options, omitValues);
1473 }
1474
1475
1476
1477 /**
1478 * Retrieves a copy of the requested operational attribute
1479 * element(s) for the specified attribute type. The list returned
1480 * may include multiple elements if the same attribute exists in
1481 * the entry multiple times with different sets of options.
1482 *
1483 * @param attributeType The attribute type to retrieve.
1484 * @param options The set of attribute options to include in
1485 * matching elements.
1486 * @param omitValues <CODE>true</CODE> if the values are to be
1487 * omitted.
1488 *
1489 * @return A copy of the requested attribute element(s) for the
1490 * specified attribute type, or <CODE>null</CODE> if there
1491 * is no such user attribute with the specified set of
1492 * options.
1493 */
1494 public List<Attribute> duplicateOperationalAttribute(
1495 AttributeType attributeType,
1496 Set<String> options,
1497 boolean omitValues)
1498 {
1499 List<Attribute> currentList =
1500 getOperationalAttribute(attributeType);
1501 return duplicateAttribute(currentList, options, omitValues);
1502 }
1503
1504
1505 /**
1506 * Indicates whether this entry contains the specified operational
1507 * attribute.
1508 *
1509 * @param attributeType The attribute type for which to make the
1510 * determination.
1511 *
1512 * @return <CODE>true</CODE> if this entry contains the specified
1513 * operational attribute, or <CODE>false</CODE> if not.
1514 */
1515 public boolean hasOperationalAttribute(AttributeType attributeType)
1516 {
1517 if (operationalAttributes.containsKey(attributeType))
1518 {
1519 return true;
1520 }
1521
1522 if (attributeType.mayHaveSubordinateTypes())
1523 {
1524 for (AttributeType at : schema.getSubTypes(attributeType))
1525 {
1526 if (operationalAttributes.containsKey(at))
1527 {
1528 return true;
1529 }
1530 }
1531 }
1532
1533 return false;
1534 }
1535
1536
1537
1538 /**
1539 * Retrieves the requested operational attribute element(s) for the
1540 * specified attribute type. The list returned may include multiple
1541 * elements if the same attribute exists in the entry multiple times
1542 * with different sets of options.
1543 *
1544 * @param attributeType The attribute type to retrieve.
1545 *
1546 * @return The requested attribute element(s) for the specified
1547 * attribute type, or <CODE>null</CODE> if there is no such
1548 * operational attribute.
1549 */
1550 public List<Attribute> getOperationalAttribute(
1551 AttributeType attributeType)
1552 {
1553 if (attributeType.mayHaveSubordinateTypes())
1554 {
1555 LinkedList<Attribute> attributes = new LinkedList<Attribute>();
1556
1557 List<Attribute> attrs =
1558 operationalAttributes.get(attributeType);
1559 if (attrs != null)
1560 {
1561 attributes.addAll(attrs);
1562 }
1563
1564 for (AttributeType at : schema.getSubTypes(attributeType))
1565 {
1566 attrs = operationalAttributes.get(at);
1567 if (attrs != null)
1568 {
1569 attributes.addAll(attrs);
1570 }
1571 }
1572
1573 if (attributes.isEmpty())
1574 {
1575 return null;
1576 }
1577 else
1578 {
1579 return attributes;
1580 }
1581 }
1582 else
1583 {
1584 return operationalAttributes.get(attributeType);
1585 }
1586 }
1587
1588
1589
1590 /**
1591 * Retrieves the requested operational attribute element(s) for the
1592 * specified attribute type. The list returned may include multiple
1593 * elements if the same attribute exists in the entry multiple times
1594 * with different sets of options.
1595 *
1596 * @param attributeType The attribute type to retrieve.
1597 * @param options The set of attribute options to include in
1598 * matching elements.
1599 *
1600 * @return The requested attribute element(s) for the specified
1601 * attribute type, or <CODE>null</CODE> if there is no such
1602 * operational attribute with the specified set of options.
1603 */
1604 public List<Attribute> getOperationalAttribute(
1605 AttributeType attributeType,
1606 Set<String> options)
1607 {
1608 LinkedList<Attribute> attributes = new LinkedList<Attribute>();
1609 List<Attribute> attrs = operationalAttributes.get(attributeType);
1610 if (attrs != null)
1611 {
1612 attributes.addAll(attrs);
1613 }
1614
1615 if (attributeType.mayHaveSubordinateTypes())
1616 {
1617 for (AttributeType at : schema.getSubTypes(attributeType))
1618 {
1619 attrs = operationalAttributes.get(at);
1620 if (attrs != null)
1621 {
1622 attributes.addAll(attrs);
1623 }
1624 }
1625 }
1626
1627 Iterator<Attribute> iterator = attributes.iterator();
1628 while (iterator.hasNext())
1629 {
1630 Attribute a = iterator.next();
1631 if (! a.hasOptions(options))
1632 {
1633 iterator.remove();
1634 }
1635 }
1636
1637 if (attributes.isEmpty())
1638 {
1639 return null;
1640 }
1641 else
1642 {
1643 return attributes;
1644 }
1645 }
1646
1647
1648
1649 /**
1650 * Retrieves a duplicate of the operational attribute list for the
1651 * specified type.
1652 *
1653 * @param attributeType The attribute type for which to retrieve a
1654 * duplicate attribute list.
1655 *
1656 * @return A duplicate of the requested attribute list, or
1657 * <CODE>null</CODE> if there is no such operational
1658 * attribute.
1659 */
1660 public List<Attribute> duplicateOperationalAttribute(
1661 AttributeType attributeType)
1662 {
1663 LinkedList<Attribute> attributes = new LinkedList<Attribute>();
1664
1665 List<Attribute> attrs = operationalAttributes.get(attributeType);
1666 if (attrs != null)
1667 {
1668 for (Attribute a : attrs)
1669 {
1670 attributes.add(a.duplicate());
1671 }
1672 }
1673
1674 if (attributeType.mayHaveSubordinateTypes())
1675 {
1676 for (AttributeType at : schema.getSubTypes(attributeType))
1677 {
1678 attrs = operationalAttributes.get(at);
1679 if (attrs != null)
1680 {
1681 for (Attribute a : attrs)
1682 {
1683 attributes.add(a.duplicate());
1684 }
1685 }
1686 }
1687 }
1688
1689 if (attributes.isEmpty())
1690 {
1691 return null;
1692 }
1693 else
1694 {
1695 return attributes;
1696 }
1697 }
1698
1699
1700 /**
1701 * Puts the provided attribute in this entry. If an attribute
1702 * already exists with the provided type, it will be overwritten.
1703 * Otherwise, a new attribute will be added. Note that no
1704 * validation will be performed.
1705 *
1706 * @param attributeType The attribute type for the set of
1707 * attributes to add.
1708 * @param attributeList The set of attributes to add for the given
1709 * type.
1710 */
1711 public void putAttribute(AttributeType attributeType,
1712 List<Attribute> attributeList)
1713 {
1714 attachment = null;
1715
1716
1717 // See if there is already a set of attributes with the specified
1718 // type. If so, then overwrite it.
1719 List<Attribute> attrList = userAttributes.get(attributeType);
1720 if (attrList != null)
1721 {
1722 userAttributes.put(attributeType, attributeList);
1723 return;
1724 }
1725
1726 attrList = operationalAttributes.get(attributeType);
1727 if (attrList != null)
1728 {
1729 operationalAttributes.put(attributeType, attributeList);
1730 return;
1731 }
1732
1733
1734 // This is a new attribute, so add it to the set of user or
1735 // operational attributes as appropriate.
1736 if (attributeType.isOperational())
1737 {
1738 operationalAttributes.put(attributeType, attributeList);
1739 }
1740 else
1741 {
1742 userAttributes.put(attributeType, attributeList);
1743 }
1744 }
1745
1746
1747
1748 /**
1749 * Adds the provided attribute to this entry. If an attribute with
1750 * the provided type already exists, then the values will be merged.
1751 *
1752 * @param attribute The attribute to add or merge with this
1753 * entry.
1754 * @param duplicateValues A list to which any duplicate values
1755 * will be added.
1756 */
1757 public void addAttribute(Attribute attribute,
1758 List<AttributeValue> duplicateValues)
1759 {
1760 attachment = null;
1761
1762 List<Attribute> attrList =
1763 getAttribute(attribute.getAttributeType(), false);
1764 if (attrList == null)
1765 {
1766 // There are no instances of the specified attribute in this
1767 // entry, so simply add it.
1768 attrList = new ArrayList<Attribute>(1);
1769 attrList.add(attribute);
1770
1771 AttributeType attrType = attribute.getAttributeType();
1772 if (attrType.isOperational())
1773 {
1774 operationalAttributes.put(attrType, attrList);
1775 }
1776 else
1777 {
1778 userAttributes.put(attrType, attrList);
1779 }
1780
1781 return;
1782 }
1783 else
1784 {
1785 // There are some instances of this attribute, but they may not
1786 // have exactly the same set of options. See if we can find an
1787 // attribute with the same set of options to merge in the
1788 // values. If not, then add the new attribute to the list.
1789 HashSet<String> options = attribute.getOptions();
1790 for (Attribute a : attrList)
1791 {
1792 if (a.optionsEqual(options))
1793 {
1794 // There is an attribute with the same set of options.
1795 // Merge the value lists together.
1796 LinkedHashSet<AttributeValue> existingValues =
1797 a.getValues();
1798 LinkedHashSet<AttributeValue> newValues =
1799 attribute.getValues();
1800 for (AttributeValue v : newValues)
1801 {
1802 if (! existingValues.add(v))
1803 {
1804 duplicateValues.add(v);
1805 }
1806 }
1807
1808 return;
1809 }
1810 }
1811
1812 attrList.add(attribute);
1813 }
1814 }
1815
1816
1817
1818 /**
1819 * Removes all instances of the specified attribute type from this
1820 * entry, including any instances with options. If the provided
1821 * attribute type is the objectclass type, then all objectclass
1822 * values will be removed (but must be replaced for the entry to
1823 * be valid). If the specified attribute type is not present in
1824 * this entry, then this method will have no effect.
1825 *
1826 * @param attributeType The attribute type for the attribute to
1827 * remove from this entry.
1828 *
1829 * @return <CODE>true</CODE> if the attribute was found and
1830 * removed, or <CODE>false</CODE> if it was not present in
1831 * the entry.
1832 */
1833 public boolean removeAttribute(AttributeType attributeType)
1834 {
1835 attachment = null;
1836
1837 if (attributeType.isObjectClassType())
1838 {
1839 objectClasses.clear();
1840 return true;
1841 }
1842 else
1843 {
1844 return ((userAttributes.remove(attributeType) != null) ||
1845 (operationalAttributes.remove(attributeType) != null));
1846 }
1847 }
1848
1849
1850
1851 /**
1852 * Removes the attribute with the provided type and set of options
1853 * from this entry. Only the instance with the exact set of
1854 * options provided will be removed. This has no effect if the
1855 * specified attribute is not present in this entry with the given
1856 * set of options.
1857 *
1858 * @param attributeType The attribute type for the attribute to
1859 * remove from this entry.
1860 * @param options The set of attribute options to use when
1861 * determining which attribute to remove.
1862 *
1863 * @return <CODE>true</CODE> if the attribute was found and
1864 * removed, or <CODE>false</CODE> if it was not present in
1865 * the entry.
1866 */
1867 public boolean removeAttribute(AttributeType attributeType,
1868 Set<String> options)
1869 {
1870 attachment = null;
1871
1872 List<Attribute> attrList = userAttributes.get(attributeType);
1873 if (attrList == null)
1874 {
1875 attrList = operationalAttributes.get(attributeType);
1876 if (attrList == null)
1877 {
1878 return false;
1879 }
1880 }
1881
1882 boolean removed = false;
1883
1884 Iterator<Attribute> iterator = attrList.iterator();
1885 while (iterator.hasNext())
1886 {
1887 Attribute a = iterator.next();
1888 if (a.optionsEqual(options))
1889 {
1890 iterator.remove();
1891 removed = true;
1892 break;
1893 }
1894 }
1895
1896 if (attrList.isEmpty())
1897 {
1898 userAttributes.remove(attributeType);
1899 operationalAttributes.remove(attributeType);
1900 }
1901
1902 return removed;
1903 }
1904
1905
1906
1907 /**
1908 * Removes the provided attribute from this entry. If the given
1909 * attribute does not have any values, then all values of the
1910 * associated attribute type (taking into account the options in the
1911 * provided type) will be removed. Otherwise, only the specified
1912 * values will be removed.
1913 *
1914 * @param attribute The attribute containing the information
1915 * to use to perform the removal.
1916 * @param missingValues A list to which any values contained in
1917 * the provided attribute but not present in
1918 * the entry will be added.
1919 *
1920 * @return <CODE>true</CODE> if the attribute type was present and
1921 * the specified values that were present were removed, or
1922 * <CODE>false</CODE> if the attribute type was not present
1923 * in the entry. If the attribute type was present but
1924 * only contained some of the values in the provided
1925 * attribute, then this method will return
1926 * <CODE>true</CODE> but will add those values to the
1927 * provided list.
1928 */
1929 public boolean removeAttribute(Attribute attribute,
1930 List<AttributeValue> missingValues)
1931 {
1932 attachment = null;
1933
1934
1935 if (attribute.getAttributeType().isObjectClassType())
1936 {
1937 LinkedHashSet<AttributeValue> valueSet = attribute.getValues();
1938 if ((valueSet == null) || valueSet.isEmpty())
1939 {
1940 objectClasses.clear();
1941 return true;
1942 }
1943
1944 boolean allSuccessful = true;
1945
1946 for (AttributeValue v : attribute.getValues())
1947 {
1948 String ocName;
1949 try
1950 {
1951 ocName = v.getNormalizedStringValue();
1952 }
1953 catch (Exception e)
1954 {
1955 if (debugEnabled())
1956 {
1957 TRACER.debugCaught(DebugLogLevel.ERROR, e);
1958 }
1959
1960 ocName = toLowerCase(v.getStringValue());
1961 }
1962
1963 boolean matchFound = false;
1964
1965 for (ObjectClass oc : objectClasses.keySet())
1966 {
1967 if (oc.hasNameOrOID(ocName))
1968 {
1969 matchFound = true;
1970 objectClasses.remove(oc);
1971 break;
1972 }
1973 }
1974
1975 if (! matchFound)
1976 {
1977 allSuccessful = false;
1978 missingValues.add(v);
1979 }
1980 }
1981
1982 return allSuccessful;
1983 }
1984
1985
1986 if (attribute.hasOptions())
1987 {
1988 HashSet<String> options = attribute.getOptions();
1989
1990 LinkedHashSet<AttributeValue> valueSet = attribute.getValues();
1991 if ((valueSet == null) || valueSet.isEmpty())
1992 {
1993 return removeAttribute(attribute.getAttributeType(), options);
1994 }
1995
1996 List<Attribute> attrList =
1997 getAttribute(attribute.getAttributeType(), false);
1998 if (attrList == null)
1999 {
2000 return false;
2001 }
2002
2003 for (Attribute a : attrList)
2004 {
2005 if (a.optionsEqual(options))
2006 {
2007 LinkedHashSet<AttributeValue> existingValueSet =
2008 a.getValues();
2009
2010 for (AttributeValue v : valueSet)
2011 {
2012 if (! existingValueSet.remove(v))
2013 {
2014 missingValues.add(v);
2015 }
2016 }
2017
2018 if (existingValueSet.isEmpty())
2019 {
2020 return removeAttribute(attribute.getAttributeType(),
2021 options);
2022 }
2023
2024 return true;
2025 }
2026 }
2027
2028 return false;
2029 }
2030 else
2031 {
2032 LinkedHashSet<AttributeValue> valueSet = attribute.getValues();
2033 if ((valueSet == null) || valueSet.isEmpty())
2034 {
2035 return removeAttribute(attribute.getAttributeType(), null);
2036 }
2037
2038 List<Attribute> attrList =
2039 getAttribute(attribute.getAttributeType(), false);
2040 if (attrList == null)
2041 {
2042 return false;
2043 }
2044
2045 for (Attribute a : attrList)
2046 {
2047 if (! a.hasOptions())
2048 {
2049 LinkedHashSet<AttributeValue> existingValueSet =
2050 a.getValues();
2051
2052 for (AttributeValue v : valueSet)
2053 {
2054 if (! existingValueSet.remove(v))
2055 {
2056 missingValues.add(v);
2057 }
2058 }
2059
2060 if (existingValueSet.isEmpty())
2061 {
2062 return removeAttribute(attribute.getAttributeType(),
2063 null);
2064 }
2065
2066 return true;
2067 }
2068 }
2069
2070 return false;
2071 }
2072 }
2073
2074
2075
2076 /**
2077 * Indicates whether the specified attribute type is allowed by any
2078 * of the objectclasses associated with this entry.
2079 *
2080 * @param attributeType The attribute type for which to make the
2081 * determination.
2082 *
2083 * @return <CODE>true</CODE> if the specified attribute is allowed
2084 * by any of the objectclasses associated with this entry,
2085 * or <CODE>false</CODE> if it is not.
2086 */
2087 public boolean allowsAttribute(AttributeType attributeType)
2088 {
2089 for (ObjectClass o : objectClasses.keySet())
2090 {
2091 if (o.isRequiredOrOptional(attributeType))
2092 {
2093 return true;
2094 }
2095 }
2096
2097 return false;
2098 }
2099
2100
2101
2102 /**
2103 * Indicates whether the specified attribute type is required by any
2104 * of the objectclasses associated with this entry.
2105 *
2106 * @param attributeType The attribute type for which to make the
2107 * determination.
2108 *
2109 * @return <CODE>true</CODE> if the specified attribute is required
2110 * by any of the objectclasses associated with this entry,
2111 * o r<CODE>false</CODE> if it is not.
2112 */
2113 public boolean requiresAttribute(AttributeType attributeType)
2114 {
2115 for (ObjectClass o : objectClasses.keySet())
2116 {
2117 if (o.isRequired(attributeType))
2118 {
2119 return true;
2120 }
2121 }
2122
2123 return false;
2124 }
2125
2126
2127
2128 /**
2129 * Indicates whether this entry contains the specified attribute
2130 * value.
2131 *
2132 * @param attributeType The attribute type for the attribute.
2133 * @param options The set of options for the attribute.
2134 * @param value The value for the attribute.
2135 *
2136 * @return <CODE>true</CODE> if this entry contains the specified
2137 * attribute value, or <CODE>false</CODE> if it does not.
2138 */
2139 public boolean hasValue(AttributeType attributeType,
2140 Set<String> options, AttributeValue value)
2141 {
2142 List<Attribute> attrList = getAttribute(attributeType, true);
2143 if ((attrList == null) || attrList.isEmpty())
2144 {
2145 return false;
2146 }
2147
2148 for (Attribute a : attrList)
2149 {
2150 if (a.optionsEqual(options))
2151 {
2152 return a.hasValue(value);
2153 }
2154 }
2155
2156 return false;
2157 }
2158
2159
2160
2161 /**
2162 * Applies the provided modification to this entry. No schema
2163 * checking will be performed.
2164 *
2165 * @param mod The modification to apply to this entry.
2166 *
2167 * @throws DirectoryException If a problem occurs while attempting
2168 * to apply the modification. Note
2169 * that even if a problem occurs, then
2170 * the entry may have been altered in
2171 * some way.
2172 */
2173 public void applyModification(Modification mod)
2174 throws DirectoryException
2175 {
2176 Attribute a = mod.getAttribute();
2177 AttributeType t = a.getAttributeType();
2178
2179 // We'll need to handle changes to the objectclass attribute in a
2180 // special way.
2181 if (t.isObjectClassType())
2182 {
2183 LinkedHashMap<ObjectClass,String> ocs = new
2184 LinkedHashMap<ObjectClass,String>();
2185 for (AttributeValue v : a.getValues())
2186 {
2187 String ocName = v.getStringValue();
2188 String lowerName = toLowerCase(ocName);
2189 ObjectClass oc =
2190 DirectoryServer.getObjectClass(lowerName, true);
2191 ocs.put(oc, ocName);
2192 }
2193
2194 switch (mod.getModificationType())
2195 {
2196 case ADD:
2197 for (ObjectClass oc : ocs.keySet())
2198 {
2199 if (objectClasses.containsKey(oc))
2200 {
2201 Message message =
2202 ERR_ENTRY_DUPLICATE_VALUES.get(a.getName());
2203 throw new DirectoryException(
2204 ResultCode.ATTRIBUTE_OR_VALUE_EXISTS,
2205 message);
2206 }
2207 else
2208 {
2209 objectClasses.put(oc, ocs.get(oc));
2210 }
2211 }
2212 break;
2213
2214 case DELETE:
2215 for (ObjectClass oc : ocs.keySet())
2216 {
2217 if (objectClasses.remove(oc) == null)
2218 {
2219 Message message =
2220 ERR_ENTRY_NO_SUCH_VALUE.get(a.getName());
2221 throw new DirectoryException(
2222 ResultCode.NO_SUCH_ATTRIBUTE, message);
2223 }
2224 }
2225 break;
2226
2227 case REPLACE:
2228 objectClasses = ocs;
2229 break;
2230
2231 case INCREMENT:
2232 Message message =
2233 ERR_ENTRY_OC_INCREMENT_NOT_SUPPORTED.get();
2234 throw new DirectoryException(
2235 ResultCode.UNWILLING_TO_PERFORM, message);
2236
2237 default:
2238 message = ERR_ENTRY_UNKNOWN_MODIFICATION_TYPE.get(
2239 String.valueOf(mod.getModificationType()));
2240 throw new DirectoryException(
2241 ResultCode.UNWILLING_TO_PERFORM, message);
2242 }
2243
2244 return;
2245 }
2246
2247 switch (mod.getModificationType())
2248 {
2249 case ADD:
2250 LinkedList<AttributeValue> duplicateValues =
2251 new LinkedList<AttributeValue>();
2252 addAttribute(a, duplicateValues);
2253 if (! duplicateValues.isEmpty())
2254 {
2255 Message message =
2256 ERR_ENTRY_DUPLICATE_VALUES.get(a.getName());
2257 throw new DirectoryException(
2258 ResultCode.ATTRIBUTE_OR_VALUE_EXISTS,
2259 message);
2260 }
2261 break;
2262
2263 case DELETE:
2264 LinkedList<AttributeValue> missingValues =
2265 new LinkedList<AttributeValue>();
2266 removeAttribute(a, missingValues);
2267 if (! missingValues.isEmpty())
2268 {
2269 Message message = ERR_ENTRY_NO_SUCH_VALUE.get(a.getName());
2270 throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE,
2271 message);
2272 }
2273 break;
2274
2275 case REPLACE:
2276 removeAttribute(t, a.getOptions());
2277
2278 if (a.hasValue())
2279 {
2280 // We know that we won't have any duplicate values, so we
2281 // don't kneed to worry about checking for them.
2282 duplicateValues = new LinkedList<AttributeValue>();
2283 addAttribute(a, duplicateValues);
2284 }
2285 break;
2286
2287 case INCREMENT:
2288 List<Attribute> attrList = getAttribute(t, false);
2289 if ((attrList == null) || attrList.isEmpty())
2290 {
2291 Message message =
2292 ERR_ENTRY_INCREMENT_NO_SUCH_ATTRIBUTE.get(a.getName());
2293 throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE,
2294 message);
2295 }
2296 else if (attrList.size() != 1)
2297 {
2298 Message message =
2299 ERR_ENTRY_INCREMENT_MULTIPLE_VALUES.get(a.getName());
2300 throw new DirectoryException(
2301 ResultCode.CONSTRAINT_VIOLATION, message);
2302 }
2303
2304 LinkedHashSet<AttributeValue> values =
2305 attrList.get(0).getValues();
2306 if (values.isEmpty())
2307 {
2308 Message message =
2309 ERR_ENTRY_INCREMENT_NO_SUCH_ATTRIBUTE.get(a.getName());
2310 throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE,
2311 message);
2312 }
2313 else if (values.size() > 1)
2314 {
2315 Message message =
2316 ERR_ENTRY_INCREMENT_MULTIPLE_VALUES.get(a.getName());
2317 throw new DirectoryException(
2318 ResultCode.CONSTRAINT_VIOLATION, message);
2319 }
2320
2321 LinkedHashSet<AttributeValue> newValues = a.getValues();
2322 if (newValues.size() != 1)
2323 {
2324 Message message = ERR_ENTRY_INCREMENT_INVALID_VALUE_COUNT.
2325 get(a.getName());
2326 throw new DirectoryException(
2327 ResultCode.CONSTRAINT_VIOLATION, message);
2328 }
2329
2330 long newValue;
2331 try
2332 {
2333 String s = values.iterator().next().getStringValue();
2334 long currentValue = Long.parseLong(s);
2335
2336 s = a.getValues().iterator().next().getStringValue();
2337 long increment = Long.parseLong(s);
2338
2339 newValue = currentValue+increment;
2340 }
2341 catch (NumberFormatException nfe)
2342 {
2343 Message message = ERR_ENTRY_INCREMENT_CANNOT_PARSE_AS_INT.
2344 get(a.getName());
2345 throw new DirectoryException(
2346 ResultCode.CONSTRAINT_VIOLATION, message);
2347 }
2348
2349 values.clear();
2350 values.add(new AttributeValue(t, String.valueOf(newValue)));
2351 break;
2352
2353 default:
2354 Message message = ERR_ENTRY_UNKNOWN_MODIFICATION_TYPE.get(
2355 String.valueOf(mod.getModificationType()));
2356 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
2357 message);
2358 }
2359 }
2360
2361
2362
2363 /**
2364 * Applies all of the provided modifications to this entry.
2365 *
2366 * @param mods The modifications to apply to this entry.
2367 *
2368 * @throws DirectoryException If a problem occurs while attempting
2369 * to apply the modifications. Note
2370 * that even if a problem occurs, then
2371 * the entry may have been altered in
2372 * some way.
2373 */
2374 public void applyModifications(List<Modification> mods)
2375 throws DirectoryException
2376 {
2377 for (Modification m : mods)
2378 {
2379 applyModification(m);
2380 }
2381 }
2382
2383
2384
2385 /**
2386 * Indicates whether this entry conforms to the server's schema
2387 * requirements. The checks performed by this method include:
2388 *
2389 * <UL>
2390 * <LI>Make sure that all required attributes are present, either
2391 * in the list of user or operational attributes.</LI>
2392 * <LI>Make sure that all user attributes are allowed by at least
2393 * one of the objectclasses. The operational attributes will
2394 * not be checked in this manner.</LI>
2395 * <LI>Make sure that all single-valued attributes contained in
2396 * the entry have only a single value.</LI>
2397 * <LI>Make sure that the entry contains a single structural
2398 * objectclass.</LI>
2399 * <LI>Make sure that the entry complies with any defined name
2400 * forms, DIT content rules, and DIT structure rules.</LI>
2401 * </UL>
2402 *
2403 * @param parentEntry The entry that is the immediate
2404 * parent of this entry, which may
2405 * be checked for DIT structure rule
2406 * conformance. This may be
2407 * {@code null} if there is no
2408 * parent or if it is unavailable
2409 * to the caller.
2410 * @param parentProvided Indicates whether the caller
2411 * attempted to provide the parent.
2412 * If not, then the parent entry
2413 * will be loaded on demand if it is
2414 * required.
2415 * @param validateNameForms Indicates whether to validate the
2416 * entry against name form
2417 * definitions. This should only be
2418 * {@code true} for add and modify
2419 * DN operations, as well as for
2420 * for imports.
2421 * @param validateStructureRules Indicates whether to validate the
2422 * entry against DIT structure rule
2423 * definitions. This should only
2424 * be {@code true} for add and
2425 * modify DN operations.
2426 * @param invalidReason The buffer to which an
2427 * explanation will be appended if
2428 * this entry does not conform to
2429 * the server's schema
2430 * configuration.
2431 *
2432 * @return {@code true} if this entry conforms to the server's
2433 * schema requirements, or {@code false} if it does not.
2434 */
2435 public boolean conformsToSchema(Entry parentEntry,
2436 boolean parentProvided,
2437 boolean validateNameForms,
2438 boolean validateStructureRules,
2439 MessageBuilder invalidReason)
2440 {
2441 // Get the structural objectclass for the entry. If there isn't
2442 // one, or if there's more than one, then see if that's OK.
2443 AcceptRejectWarn structuralPolicy =
2444 DirectoryServer.getSingleStructuralObjectClassPolicy();
2445 ObjectClass structuralClass = null;
2446 boolean multipleOCErrorLogged = false;
2447 for (ObjectClass oc : objectClasses.keySet())
2448 {
2449 if (oc.getObjectClassType() == ObjectClassType.STRUCTURAL)
2450 {
2451 if ((structuralClass == null) ||
2452 oc.isDescendantOf(structuralClass))
2453 {
2454 structuralClass = oc;
2455 }
2456 else if (! structuralClass.isDescendantOf(oc))
2457 {
2458 Message message =
2459 ERR_ENTRY_SCHEMA_MULTIPLE_STRUCTURAL_CLASSES.get(
2460 String.valueOf(dn),
2461 structuralClass.getNameOrOID(),
2462 oc.getNameOrOID());
2463
2464 if (structuralPolicy == AcceptRejectWarn.REJECT)
2465 {
2466 invalidReason.append(message);
2467 return false;
2468 }
2469 else if (structuralPolicy == AcceptRejectWarn.WARN)
2470 {
2471 if (! multipleOCErrorLogged)
2472 {
2473 logError(message);
2474 multipleOCErrorLogged = true;
2475 }
2476 }
2477 }
2478 }
2479 }
2480
2481 NameForm nameForm = null;
2482 DITContentRule ditContentRule = null;
2483 DITStructureRule ditStructureRule = null;
2484 if (structuralClass == null)
2485 {
2486 Message message = ERR_ENTRY_SCHEMA_NO_STRUCTURAL_CLASS.get(
2487 String.valueOf(dn));
2488
2489 if (structuralPolicy == AcceptRejectWarn.REJECT)
2490 {
2491 invalidReason.append(message);
2492 return false;
2493 }
2494 else if (structuralPolicy == AcceptRejectWarn.WARN)
2495 {
2496 logError(message);
2497 }
2498 }
2499 else
2500 {
2501 ditContentRule =
2502 DirectoryServer.getDITContentRule(structuralClass);
2503 if ((ditContentRule != null) && ditContentRule.isObsolete())
2504 {
2505 ditContentRule = null;
2506 }
2507
2508 if (validateNameForms)
2509 {
2510 nameForm = DirectoryServer.getNameForm(structuralClass);
2511 if ((nameForm != null) && nameForm.isObsolete())
2512 {
2513 nameForm = null;
2514 }
2515
2516 if (validateStructureRules && (nameForm != null))
2517 {
2518 ditStructureRule =
2519 DirectoryServer.getDITStructureRule(nameForm);
2520 if ((ditStructureRule != null) &&
2521 ditStructureRule.isObsolete())
2522 {
2523 ditStructureRule = null;
2524 }
2525 }
2526 }
2527 }
2528
2529
2530 if (! checkAttributesAndObjectClasses(ditContentRule,
2531 structuralPolicy, invalidReason))
2532 {
2533 return false;
2534 }
2535
2536
2537 // If there is a name form for this entry, then make sure that the
2538 // RDN for the entry is in compliance with it.
2539 if (nameForm != null)
2540 {
2541 if (! checkNameForm(nameForm, structuralPolicy, invalidReason))
2542 {
2543 return false;
2544 }
2545 }
2546
2547
2548 // If there is a DIT content rule for this entry, then make sure
2549 // that the entry is in compliance with it.
2550 if (ditContentRule != null)
2551 {
2552 if (! checkDITContentRule(ditContentRule, structuralPolicy,
2553 invalidReason))
2554 {
2555 return false;
2556 }
2557 }
2558
2559
2560 if (! checkDITStructureRule(ditStructureRule, structuralClass,
2561 parentEntry, parentProvided, validateStructureRules,
2562 structuralPolicy, invalidReason))
2563 {
2564 return false;
2565 }
2566
2567
2568 // If we've gotten here, then the entry is acceptable.
2569 return true;
2570 }
2571
2572
2573
2574 /**
2575 * Checks the attributes and object classes contained in this entry
2576 * to determine whether they conform to the server schema
2577 * requirements.
2578 *
2579 * @param ditContentRule The DIT content rule for this entry, if
2580 * any.
2581 * @param structuralPolicy The policy that should be used for
2582 * structural object class compliance.
2583 * @param invalidReason A buffer into which an invalid reason
2584 * may be added.
2585 *
2586 * @return {@code true} if this entry passes all of the checks, or
2587 * {@code false} if there are any failures.
2588 */
2589 private boolean checkAttributesAndObjectClasses(
2590 DITContentRule ditContentRule,
2591 AcceptRejectWarn structuralPolicy,
2592 MessageBuilder invalidReason)
2593 {
2594 // Make sure that we recognize all of the objectclasses, that all
2595 // auxiliary classes are allowed by the DIT content rule, and that
2596 // all attributes required by the object classes are present.
2597 for (ObjectClass o : objectClasses.keySet())
2598 {
2599 if (DirectoryServer.getObjectClass(o.getOID()) == null)
2600 {
2601 Message message = ERR_ENTRY_SCHEMA_UNKNOWN_OC.get(
2602 String.valueOf(dn), o
2603 .getNameOrOID());
2604 invalidReason.append(message);
2605 return false;
2606 }
2607
2608 if ((o.getObjectClassType() == ObjectClassType.AUXILIARY) &&
2609 (ditContentRule != null) &&
2610 (! ditContentRule.getAuxiliaryClasses().contains(o)))
2611 {
2612 Message message =
2613 ERR_ENTRY_SCHEMA_DISALLOWED_AUXILIARY_CLASS.get(
2614 String.valueOf(dn),
2615 o.getNameOrOID(),
2616 ditContentRule.getName());
2617 if (structuralPolicy == AcceptRejectWarn.REJECT)
2618 {
2619 invalidReason.append(message);
2620 return false;
2621 }
2622 else if (structuralPolicy == AcceptRejectWarn.WARN)
2623 {
2624 logError(message);
2625 }
2626 }
2627
2628 for (AttributeType t : o.getRequiredAttributes())
2629 {
2630 if (! (userAttributes.containsKey(t) ||
2631 operationalAttributes.containsKey(t) ||
2632 t.isObjectClassType()))
2633 {
2634 Message message =
2635 ERR_ENTRY_SCHEMA_MISSING_REQUIRED_ATTR_FOR_OC.get(
2636 String.valueOf(dn),
2637 t.getNameOrOID(),
2638 o.getNameOrOID());
2639 invalidReason.append(message);
2640 return false;
2641 }
2642 }
2643 }
2644
2645
2646 // Make sure all the user attributes are allowed, have at least
2647 // one value, and if they are single-valued that they have exactly
2648 // one value.
2649 for (AttributeType t : userAttributes.keySet())
2650 {
2651 boolean found = false;
2652 for (ObjectClass o : objectClasses.keySet())
2653 {
2654 if (o.isRequiredOrOptional(t))
2655 {
2656 found = true;
2657 break;
2658 }
2659 }
2660
2661 if ((! found) && (ditContentRule != null))
2662 {
2663 if (ditContentRule.isRequiredOrOptional(t))
2664 {
2665 found = true;
2666 }
2667 }
2668
2669 if (! found)
2670 {
2671 Message message =
2672 ERR_ENTRY_SCHEMA_DISALLOWED_USER_ATTR_FOR_OC.get(
2673 String.valueOf(dn),
2674 t.getNameOrOID());
2675 invalidReason.append(message);
2676 return false;
2677 }
2678
2679 List<Attribute> attrList = userAttributes.get(t);
2680 if (attrList != null)
2681 {
2682 for (Attribute a : attrList)
2683 {
2684 LinkedHashSet<AttributeValue> values = a.getValues();
2685 if (values.isEmpty())
2686 {
2687 Message message = ERR_ENTRY_SCHEMA_ATTR_NO_VALUES.get(
2688 String.valueOf(dn),
2689 t.getNameOrOID());
2690
2691 invalidReason.append(message);
2692 return false;
2693 }
2694 else if (t.isSingleValue() && (values.size() != 1))
2695 {
2696 Message message = ERR_ENTRY_SCHEMA_ATTR_SINGLE_VALUED.get(
2697 String.valueOf(dn),
2698 t.getNameOrOID());
2699
2700 invalidReason.append(message);
2701 return false;
2702 }
2703 }
2704 }
2705 }
2706
2707
2708 // Iterate through all of the operational attributes and make sure
2709 // that all of the single-valued attributes only have one value.
2710 for (AttributeType t : operationalAttributes.keySet())
2711 {
2712 if (t.isSingleValue())
2713 {
2714 List<Attribute> attrList = operationalAttributes.get(t);
2715 if (attrList != null)
2716 {
2717 for (Attribute a : attrList)
2718 {
2719 if (a.getValues().size() > 1)
2720 {
2721 Message message =
2722 ERR_ENTRY_SCHEMA_ATTR_SINGLE_VALUED.get(
2723 String.valueOf(dn),
2724 t.getNameOrOID());
2725
2726 invalidReason.append(message);
2727 return false;
2728 }
2729 }
2730 }
2731 }
2732 }
2733
2734
2735 // If we've gotten here, then things are OK.
2736 return true;
2737 }
2738
2739
2740
2741 /**
2742 * Performs any processing needed for name form validation.
2743 *
2744 * @param nameForm The name form to validate against this
2745 * entry.
2746 * @param structuralPolicy The policy that should be used for
2747 * structural object class compliance.
2748 * @param invalidReason A buffer into which an invalid reason
2749 * may be added.
2750 *
2751 * @return {@code true} if this entry passes all of the checks, or
2752 * {@code false} if there are any failures.
2753 */
2754 private boolean checkNameForm(NameForm nameForm,
2755 AcceptRejectWarn structuralPolicy,
2756 MessageBuilder invalidReason)
2757 {
2758 RDN rdn = dn.getRDN();
2759 if (rdn != null)
2760 {
2761 // Make sure that all the required attributes are present.
2762 for (AttributeType t : nameForm.getRequiredAttributes())
2763 {
2764 if (! rdn.hasAttributeType(t))
2765 {
2766 Message message =
2767 ERR_ENTRY_SCHEMA_RDN_MISSING_REQUIRED_ATTR.get(
2768 String.valueOf(dn),
2769 t.getNameOrOID(),
2770 nameForm.getNameOrOID());
2771
2772 if (structuralPolicy == AcceptRejectWarn.REJECT)
2773 {
2774 invalidReason.append(message);
2775 return false;
2776 }
2777 else if (structuralPolicy == AcceptRejectWarn.WARN)
2778 {
2779 logError(message);
2780 }
2781 }
2782 }
2783
2784 // Make sure that all attributes in the RDN are allowed.
2785 int numAVAs = rdn.getNumValues();
2786 for (int i = 0; i < numAVAs; i++)
2787 {
2788 AttributeType t = rdn.getAttributeType(i);
2789 if (! nameForm.isRequiredOrOptional(t))
2790 {
2791 Message message =
2792 ERR_ENTRY_SCHEMA_RDN_DISALLOWED_ATTR.get(
2793 String.valueOf(dn),
2794 t.getNameOrOID(),
2795 nameForm.getNameOrOID());
2796
2797 if (structuralPolicy == AcceptRejectWarn.REJECT)
2798 {
2799 invalidReason.append(message);
2800 return false;
2801 }
2802 else if (structuralPolicy == AcceptRejectWarn.WARN)
2803 {
2804 logError(message);
2805 }
2806 }
2807 }
2808 }
2809
2810 // If we've gotten here, then things are OK.
2811 return true;
2812 }
2813
2814
2815
2816 /**
2817 * Performs any processing needed for DIT content rule validation.
2818 *
2819 * @param ditContentRule The DIT content rule to validate
2820 * against this entry.
2821 * @param structuralPolicy The policy that should be used for
2822 * structural object class compliance.
2823 * @param invalidReason A buffer into which an invalid reason
2824 * may be added.
2825 *
2826 * @return {@code true} if this entry passes all of the checks, or
2827 * {@code false} if there are any failures.
2828 */
2829 private boolean checkDITContentRule(DITContentRule ditContentRule,
2830 AcceptRejectWarn structuralPolicy,
2831 MessageBuilder invalidReason)
2832 {
2833 // Make sure that all of the required attributes are present.
2834 for (AttributeType t : ditContentRule.getRequiredAttributes())
2835 {
2836 if (! (userAttributes.containsKey(t) ||
2837 operationalAttributes.containsKey(t) ||
2838 t.isObjectClassType()))
2839 {
2840 Message message =
2841 ERR_ENTRY_SCHEMA_MISSING_REQUIRED_ATTR_FOR_DCR.get(
2842 String.valueOf(dn),
2843 t.getNameOrOID(),
2844 ditContentRule.getName());
2845
2846 if (structuralPolicy == AcceptRejectWarn.REJECT)
2847 {
2848 invalidReason.append(message);
2849 return false;
2850 }
2851 else if (structuralPolicy == AcceptRejectWarn.WARN)
2852 {
2853 logError(message);
2854 }
2855 }
2856 }
2857
2858 // Make sure that none of the prohibited attributes are present.
2859 for (AttributeType t : ditContentRule.getProhibitedAttributes())
2860 {
2861 if (userAttributes.containsKey(t) ||
2862 operationalAttributes.containsKey(t))
2863 {
2864 Message message =
2865 ERR_ENTRY_SCHEMA_PROHIBITED_ATTR_FOR_DCR.get(
2866 String.valueOf(dn),
2867 t.getNameOrOID(),
2868 ditContentRule.getName());
2869
2870 if (structuralPolicy == AcceptRejectWarn.REJECT)
2871 {
2872 invalidReason.append(message);
2873 return false;
2874 }
2875 else if (structuralPolicy == AcceptRejectWarn.WARN)
2876 {
2877 logError(message);
2878 }
2879 }
2880 }
2881
2882 // If we've gotten here, then things are OK.
2883 return true;
2884 }
2885
2886
2887
2888 /**
2889 * Performs any processing needed for DIT structure rule validation.
2890 *
2891 * @param ditStructureRule The DIT structure rule for this
2892 * entry.
2893 * @param structuralClass The structural object class for
2894 * this entry.
2895 * @param parentEntry The parent entry, if available
2896 * and applicable.
2897 * @param parentProvided Indicates whether the parent
2898 * entry was provided.
2899 * @param validateStructureRules Indicates whether to check to see
2900 * if this entry violates a DIT
2901 * structure rule for its parent.
2902 * @param structuralPolicy The policy that should be used
2903 * for structural object class
2904 * compliance.
2905 * @param invalidReason A buffer into which an invalid
2906 * reason may be added.
2907 *
2908 * @return {@code true} if this entry passes all of the checks, or
2909 * {@code false} if there are any failures.
2910 */
2911 private boolean checkDITStructureRule(
2912 DITStructureRule ditStructureRule,
2913 ObjectClass structuralClass,
2914 Entry parentEntry, boolean parentProvided,
2915 boolean validateStructureRules,
2916 AcceptRejectWarn structuralPolicy,
2917 MessageBuilder invalidReason)
2918 {
2919 // If there is a DIT structure rule for this entry, then make sure
2920 // that the entry is in compliance with it.
2921 if ((ditStructureRule != null) &&
2922 ditStructureRule.hasSuperiorRules())
2923 {
2924 if (parentProvided)
2925 {
2926 if (parentEntry != null)
2927 {
2928 boolean dsrValid =
2929 validateDITStructureRule(ditStructureRule,
2930 structuralClass, parentEntry,
2931 structuralPolicy,
2932 invalidReason);
2933 if (! dsrValid)
2934 {
2935 return false;
2936 }
2937 }
2938 }
2939 else
2940 {
2941 // Get the DN of the parent entry if possible.
2942 DN parentDN = dn.getParentDNInSuffix();
2943 if (parentDN != null)
2944 {
2945 // Get the parent entry and check its structural class.
2946 Lock lock = null;
2947 for (int i=0; i < 3; i++)
2948 {
2949 lock = LockManager.lockRead(parentDN);
2950 if (lock != null)
2951 {
2952 break;
2953 }
2954 }
2955
2956 if (lock == null)
2957 {
2958 Message message =
2959 ERR_ENTRY_SCHEMA_DSR_COULD_NOT_LOCK_PARENT.get(
2960 String.valueOf(dn), String.valueOf(parentDN));
2961
2962 if (structuralPolicy == AcceptRejectWarn.REJECT)
2963 {
2964 invalidReason.append(message);
2965 return false;
2966 }
2967 else if (structuralPolicy == AcceptRejectWarn.WARN)
2968 {
2969 logError(message);
2970 }
2971 }
2972 else
2973 {
2974 try
2975 {
2976 parentEntry = DirectoryServer.getEntry(parentDN);
2977 if (parentEntry == null)
2978 {
2979 Message message =
2980 ERR_ENTRY_SCHEMA_DSR_NO_PARENT_ENTRY.get(
2981 String.valueOf(dn),
2982 String.valueOf(parentDN));
2983
2984 if (structuralPolicy == AcceptRejectWarn.REJECT)
2985 {
2986 invalidReason.append(message);
2987 return false;
2988 }
2989 else if (structuralPolicy == AcceptRejectWarn.WARN)
2990 {
2991 logError(message);
2992 }
2993 }
2994 else
2995 {
2996 boolean dsrValid =
2997 validateDITStructureRule(ditStructureRule,
2998 structuralClass,
2999 parentEntry,
3000 structuralPolicy,
3001 invalidReason);
3002 if (! dsrValid)
3003 {
3004 return false;
3005 }
3006 }
3007 }
3008 catch (Exception e)
3009 {
3010 if (debugEnabled())
3011 {
3012 TRACER.debugCaught(DebugLogLevel.ERROR, e);
3013 }
3014
3015 Message message =
3016 ERR_ENTRY_SCHEMA_COULD_NOT_CHECK_DSR.get(
3017 String.valueOf(dn),
3018 ditStructureRule.getNameOrRuleID(),
3019 getExceptionMessage(e));
3020
3021 if (structuralPolicy == AcceptRejectWarn.REJECT)
3022 {
3023 invalidReason.append(message);
3024 return false;
3025 }
3026 else if (structuralPolicy == AcceptRejectWarn.WARN)
3027 {
3028 logError(message);
3029 }
3030 }
3031 finally
3032 {
3033 LockManager.unlock(parentDN, lock);
3034 }
3035 }
3036 }
3037 }
3038 }
3039 else if (validateStructureRules)
3040 {
3041 // There is no DIT structure rule for this entry, but there may
3042 // be one for the parent entry. If there is such a rule for the
3043 // parent entry, then this entry will not be valid.
3044 boolean parentExists = false;
3045 ObjectClass parentStructuralClass = null;
3046 if (parentEntry != null)
3047 {
3048 parentExists = true;
3049 parentStructuralClass =
3050 parentEntry.getStructuralObjectClass();
3051 }
3052 else if (! parentProvided)
3053 {
3054 DN parentDN = getDN().getParentDNInSuffix();
3055 if (parentDN != null)
3056 {
3057 // Get the parent entry and check its structural class.
3058 Lock lock = null;
3059 for (int i=0; i < 3; i++)
3060 {
3061 lock = LockManager.lockRead(parentDN);
3062 if (lock != null)
3063 {
3064 break;
3065 }
3066 }
3067
3068 if (lock == null)
3069 {
3070 Message message =
3071 ERR_ENTRY_SCHEMA_DSR_COULD_NOT_LOCK_PARENT.get(
3072 String.valueOf(dn),
3073 String.valueOf(parentDN));
3074
3075 if (structuralPolicy == AcceptRejectWarn.REJECT)
3076 {
3077 invalidReason.append(message);
3078 return false;
3079 }
3080 else if (structuralPolicy == AcceptRejectWarn.WARN)
3081 {
3082 logError(message);
3083 }
3084 }
3085 else
3086 {
3087 try
3088 {
3089 parentEntry = DirectoryServer.getEntry(parentDN);
3090 if (parentEntry == null)
3091 {
3092 Message message =
3093 ERR_ENTRY_SCHEMA_DSR_NO_PARENT_ENTRY.get(
3094 String.valueOf(dn),
3095 String.valueOf(parentDN));
3096
3097 if (structuralPolicy == AcceptRejectWarn.REJECT)
3098 {
3099 invalidReason.append(message);
3100 return false;
3101 }
3102 else if (structuralPolicy == AcceptRejectWarn.WARN)
3103 {
3104 logError(message);
3105 }
3106 }
3107 else
3108 {
3109 parentExists = true;
3110 parentStructuralClass =
3111 parentEntry.getStructuralObjectClass();
3112 }
3113 }
3114 catch (Exception e)
3115 {
3116 if (debugEnabled())
3117 {
3118 TRACER.debugCaught(DebugLogLevel.ERROR, e);
3119 }
3120
3121 Message message =
3122 ERR_ENTRY_SCHEMA_COULD_NOT_CHECK_PARENT_DSR.get(
3123 String.valueOf(dn),
3124 getExceptionMessage(e));
3125
3126 if (structuralPolicy == AcceptRejectWarn.REJECT)
3127 {
3128 invalidReason.append(message);
3129 return false;
3130 }
3131 else if (structuralPolicy == AcceptRejectWarn.WARN)
3132 {
3133 logError(message);
3134 }
3135 }
3136 finally
3137 {
3138 LockManager.unlock(parentDN, lock);
3139 }
3140 }
3141 }
3142 }
3143
3144 if (parentExists)
3145 {
3146 if (parentStructuralClass == null)
3147 {
3148 Message message = ERR_ENTRY_SCHEMA_DSR_NO_PARENT_OC.get(
3149 String.valueOf(dn),
3150 String.valueOf(parentEntry.getDN()));
3151
3152 if (structuralPolicy == AcceptRejectWarn.REJECT)
3153 {
3154 invalidReason.append(message);
3155 return false;
3156 }
3157 else if (structuralPolicy == AcceptRejectWarn.WARN)
3158 {
3159 logError(message);
3160 }
3161 }
3162 else
3163 {
3164 NameForm parentNF =
3165 DirectoryServer.getNameForm(parentStructuralClass);
3166 if ((parentNF != null) && (! parentNF.isObsolete()))
3167 {
3168 DITStructureRule parentDSR =
3169 DirectoryServer.getDITStructureRule(parentNF);
3170 if ((parentDSR != null) && (! parentDSR.isObsolete()))
3171 {
3172 Message message =
3173 ERR_ENTRY_SCHEMA_VIOLATES_PARENT_DSR.get(
3174 String.valueOf(dn),
3175 String.valueOf(parentEntry.getDN()));
3176
3177 if (structuralPolicy == AcceptRejectWarn.REJECT)
3178 {
3179 invalidReason.append(message);
3180 return false;
3181 }
3182 else if (structuralPolicy == AcceptRejectWarn.WARN)
3183 {
3184 logError(message);
3185 }
3186 }
3187 }
3188 }
3189 }
3190 }
3191
3192 // If we've gotten here, then things are OK.
3193 return true;
3194 }
3195
3196
3197
3198 /**
3199 * Determines whether this entry is in conformance to the provided
3200 * DIT structure rule.
3201 *
3202 * @param dsr The DIT structure rule to use in the
3203 * determination.
3204 * @param structuralClass The structural objectclass for this
3205 * entry to use in the determination.
3206 * @param parentEntry The reference to the parent entry to
3207 * check.
3208 * @param structuralPolicy The policy that should be used around
3209 * enforcement of DIT structure rules.
3210 * @param invalidReason The buffer to which the invalid reason
3211 * should be appended if a problem is
3212 * found.
3213 *
3214 * @return <CODE>true</CODE> if this entry conforms to the provided
3215 * DIT structure rule, or <CODE>false</CODE> if not.
3216 */
3217 private boolean validateDITStructureRule(DITStructureRule dsr,
3218 ObjectClass structuralClass, Entry parentEntry,
3219 AcceptRejectWarn structuralPolicy,
3220 MessageBuilder invalidReason)
3221 {
3222 ObjectClass oc = parentEntry.getStructuralObjectClass();
3223 if (oc == null)
3224 {
3225 Message message = ERR_ENTRY_SCHEMA_DSR_NO_PARENT_OC.get(
3226 String.valueOf(dn),
3227 String.valueOf(parentEntry.getDN()));
3228
3229 if (structuralPolicy == AcceptRejectWarn.REJECT)
3230 {
3231 invalidReason.append(message);
3232 return false;
3233 }
3234 else if (structuralPolicy == AcceptRejectWarn.WARN)
3235 {
3236 logError(message);
3237 }
3238 }
3239
3240 boolean matchFound = false;
3241 for (DITStructureRule dsr2 : dsr.getSuperiorRules())
3242 {
3243 if (dsr2.getStructuralClass().equals(oc))
3244 {
3245 matchFound = true;
3246 }
3247 }
3248
3249 if (! matchFound)
3250 {
3251 Message message =
3252 ERR_ENTRY_SCHEMA_DSR_DISALLOWED_SUPERIOR_OC.get(
3253 String.valueOf(dn),
3254 dsr.getNameOrRuleID(),
3255 structuralClass.getNameOrOID(),
3256 oc.getNameOrOID());
3257
3258 if (structuralPolicy == AcceptRejectWarn.REJECT)
3259 {
3260 invalidReason.append(message);
3261 return false;
3262 }
3263 else if (structuralPolicy == AcceptRejectWarn.WARN)
3264 {
3265 logError(message);
3266 }
3267 }
3268
3269 return true;
3270 }
3271
3272
3273
3274 /**
3275 * Retrieves the attachment for this entry.
3276 *
3277 * @return The attachment for this entry, or <CODE>null</CODE> if
3278 * there is none.
3279 */
3280 public Object getAttachment()
3281 {
3282 return attachment;
3283 }
3284
3285
3286
3287 /**
3288 * Specifies the attachment for this entry. This will replace any
3289 * existing attachment that might be defined.
3290 *
3291 * @param attachment The attachment for this entry, or
3292 * <CODE>null</CODE> if there should not be an
3293 * attachment.
3294 */
3295 public void setAttachment(Object attachment)
3296 {
3297 this.attachment = attachment;
3298 }
3299
3300
3301
3302 /**
3303 * Creates a duplicate of this entry that may be altered without
3304 * impacting the information in this entry.
3305 *
3306 * @param processVirtual Indicates whether virtual attribute
3307 * processing should be performed for the
3308 * entry.
3309 *
3310 * @return A duplicate of this entry that may be altered without
3311 * impacting the information in this entry.
3312 */
3313 public Entry duplicate(boolean processVirtual)
3314 {
3315 HashMap<ObjectClass,String> objectClassesCopy =
3316 new HashMap<ObjectClass,String>(objectClasses);
3317
3318 HashMap<AttributeType,List<Attribute>> userAttrsCopy =
3319 new HashMap<AttributeType,List<Attribute>>(
3320 userAttributes.size());
3321 deepCopy(userAttributes, userAttrsCopy, false);
3322
3323 HashMap<AttributeType,List<Attribute>> operationalAttrsCopy =
3324 new HashMap<AttributeType,List<Attribute>>(
3325 operationalAttributes.size());
3326 deepCopy(operationalAttributes, operationalAttrsCopy, false);
3327
3328 for (AttributeType t : suppressedAttributes.keySet())
3329 {
3330 List<Attribute> attrList = suppressedAttributes.get(t);
3331 if (t.isOperational())
3332 {
3333 operationalAttributes.put(t, attrList);
3334 }
3335 else
3336 {
3337 userAttributes.put(t, attrList);
3338 }
3339 }
3340
3341 Entry e = new Entry(dn, objectClassesCopy, userAttrsCopy,
3342 operationalAttrsCopy);
3343 if (processVirtual)
3344 {
3345 e.processVirtualAttributes();
3346 }
3347 return e;
3348 }
3349
3350
3351
3352 /**
3353 * Creates a duplicate of this entry without any operational
3354 * attributes that may be altered without impacting the information
3355 * in this entry.
3356 *
3357 * @param typesOnly Indicates whether to include attribute
3358 * types only without values.
3359 * @param processVirtual Indicates whether virtual attribute
3360 * processing should be performed for the
3361 * entry.
3362 *
3363 * @return A duplicate of this entry that may be altered without
3364 * impacting the information in this entry and that does
3365 * not contain any operational attributes.
3366 */
3367 public Entry duplicateWithoutOperationalAttributes(
3368 boolean typesOnly, boolean processVirtual)
3369 {
3370 HashMap<ObjectClass,String> objectClassesCopy;
3371 if (typesOnly)
3372 {
3373 objectClassesCopy = new HashMap<ObjectClass,String>(0);
3374 }
3375 else
3376 {
3377 objectClassesCopy =
3378 new HashMap<ObjectClass,String>(objectClasses);
3379 }
3380
3381 HashMap<AttributeType,List<Attribute>> userAttrsCopy =
3382 new HashMap<AttributeType,List<Attribute>>(
3383 userAttributes.size());
3384 if (typesOnly)
3385 {
3386 // Make sure to include the objectClass attribute here because
3387 // it won't make it in otherwise.
3388 AttributeType ocType =
3389 DirectoryServer.getObjectClassAttributeType();
3390 ArrayList<Attribute> ocList = new ArrayList<Attribute>(1);
3391 ocList.add(new Attribute(ocType));
3392 userAttrsCopy.put(ocType, ocList);
3393 }
3394
3395 deepCopy(userAttributes, userAttrsCopy, typesOnly);
3396
3397 HashMap<AttributeType,List<Attribute>> operationalAttrsCopy =
3398 new HashMap<AttributeType,List<Attribute>>(0);
3399
3400 for (AttributeType t : suppressedAttributes.keySet())
3401 {
3402 List<Attribute> attrList = suppressedAttributes.get(t);
3403 if (! t.isOperational())
3404 {
3405 userAttributes.put(t, attrList);
3406 }
3407 }
3408
3409 Entry e = new Entry(dn, objectClassesCopy, userAttrsCopy,
3410 operationalAttrsCopy);
3411
3412 if (processVirtual)
3413 {
3414 e.processVirtualAttributes(false);
3415 }
3416
3417 return e;
3418 }
3419
3420
3421
3422 /**
3423 * Performs a deep copy from the source map to the target map. In
3424 * this case, the attributes in the list will be duplicates rather
3425 * than re-using the same reference. Virtual attributes will not be
3426 * included when making the copy.
3427 *
3428 * @param source The source map from which to obtain the
3429 * information.
3430 * @param target The target map into which to place the
3431 * copied information.
3432 * @param omitValues Indicates whether to omit attribute values
3433 * when processing.
3434 */
3435 private void deepCopy(Map<AttributeType,List<Attribute>> source,
3436 Map<AttributeType,List<Attribute>> target,
3437 boolean omitValues)
3438 {
3439 for (AttributeType t : source.keySet())
3440 {
3441 List<Attribute> sourceList = source.get(t);
3442 ArrayList<Attribute> targetList =
3443 new ArrayList<Attribute>(sourceList.size());
3444
3445 for (Attribute a : sourceList)
3446 {
3447 if (a.isVirtual())
3448 {
3449 continue;
3450 }
3451
3452 targetList.add(a.duplicate(omitValues));
3453 }
3454
3455 if (! targetList.isEmpty())
3456 {
3457 target.put(t, targetList);
3458 }
3459 }
3460 }
3461
3462
3463
3464 /**
3465 * Creates a duplicate of this entry without any attribute or
3466 * objectclass information (i.e., it will just contain the DN and
3467 * placeholders for adding attributes) and objectclasses.
3468 *
3469 * @return A duplicate of this entry that may be altered without
3470 * impacting the information in this entry and that does
3471 * not contain attribute or objectclass information.
3472 */
3473 public Entry duplicateWithoutAttributes()
3474 {
3475 HashMap<ObjectClass,String> objectClassesCopy =
3476 new HashMap<ObjectClass,String>(objectClasses.size());
3477
3478 HashMap<AttributeType,List<Attribute>> userAttrsCopy =
3479 new HashMap<AttributeType,List<Attribute>>(
3480 userAttributes.size());
3481
3482 HashMap<AttributeType,List<Attribute>> operationalAttrsCopy =
3483 new HashMap<AttributeType,List<Attribute>>(
3484 operationalAttributes.size());
3485
3486 return new Entry(dn, objectClassesCopy, userAttrsCopy,
3487 operationalAttrsCopy);
3488 }
3489
3490
3491
3492 /**
3493 * Indicates whether this entry meets the criteria to consider it a
3494 * referral (e.g., it contains the "referral" objectclass and a
3495 * "ref" attribute).
3496 *
3497 * @return <CODE>true</CODE> if this entry meets the criteria to
3498 * consider it a referral, or <CODE>false</CODE> if not.
3499 */
3500 public boolean isReferral()
3501 {
3502 ObjectClass referralOC =
3503 DirectoryServer.getObjectClass(OC_REFERRAL);
3504 if (referralOC == null)
3505 {
3506 // This should not happen -- The server doesn't have a referral
3507 // objectclass defined.
3508 if (debugEnabled())
3509 {
3510 TRACER.debugWarning(
3511 "No %s objectclass is defined in the server schema.",
3512 OC_REFERRAL);
3513 }
3514
3515 for (String ocName : objectClasses.values())
3516 {
3517 if (ocName.equalsIgnoreCase(OC_REFERRAL))
3518 {
3519 return true;
3520 }
3521 }
3522
3523 return false;
3524 }
3525
3526 if (! objectClasses.containsKey(referralOC))
3527 {
3528 return false;
3529 }
3530
3531 AttributeType referralType =
3532 DirectoryServer.getAttributeType(ATTR_REFERRAL_URL);
3533 if (referralType == null)
3534 {
3535 // This should not happen -- The server doesn't have a ref
3536 // attribute type defined.
3537 if (debugEnabled())
3538 {
3539 TRACER.debugWarning(
3540 "No %s attribute type is defined in the server schema.",
3541 ATTR_REFERRAL_URL);
3542 }
3543 return false;
3544 }
3545
3546 return (userAttributes.containsKey(referralType) ||
3547 operationalAttributes.containsKey(referralType));
3548 }
3549
3550
3551
3552 /**
3553 * Retrieves the set of referral URLs that are included in this
3554 * referral entry. This should only be called if
3555 * <CODE>isReferral()</CODE> returns <CODE>true</CODE>.
3556 *
3557 * @return The set of referral URLs that are included in this entry
3558 * if it is a referral, or <CODE>null</CODE> if it is not a
3559 * referral.
3560 */
3561 public LinkedHashSet<String> getReferralURLs()
3562 {
3563 AttributeType referralType =
3564 DirectoryServer.getAttributeType(ATTR_REFERRAL_URL);
3565 if (referralType == null)
3566 {
3567 // This should not happen -- The server doesn't have a ref
3568 // attribute type defined.
3569 if (debugEnabled())
3570 {
3571 TRACER.debugWarning(
3572 "No %s attribute type is defined in the server schema.",
3573 ATTR_REFERRAL_URL);
3574 }
3575 return null;
3576 }
3577
3578 List<Attribute> refAttrs = userAttributes.get(referralType);
3579 if (refAttrs == null)
3580 {
3581 refAttrs = operationalAttributes.get(referralType);
3582 if (refAttrs == null)
3583 {
3584 return null;
3585 }
3586 }
3587
3588 LinkedHashSet<String> referralURLs = new LinkedHashSet<String>();
3589 for (Attribute a : refAttrs)
3590 {
3591 for (AttributeValue v : a.getValues())
3592 {
3593 referralURLs.add(v.getStringValue());
3594 }
3595 }
3596
3597 return referralURLs;
3598 }
3599
3600
3601
3602 /**
3603 * Indicates whether this entry meets the criteria to consider it an
3604 * alias (e.g., it contains the "aliasObject" objectclass and a
3605 * "alias" attribute).
3606 *
3607 * @return <CODE>true</CODE> if this entry meets the criteria to
3608 * consider it an alias, or <CODE>false</CODE> if not.
3609 */
3610 public boolean isAlias()
3611 {
3612 ObjectClass aliasOC = DirectoryServer.getObjectClass(OC_ALIAS);
3613 if (aliasOC == null)
3614 {
3615 // This should not happen -- The server doesn't have an alias
3616 // objectclass defined.
3617 if (debugEnabled())
3618 {
3619 TRACER.debugWarning(
3620 "No %s objectclass is defined in the server schema.",
3621 OC_ALIAS);
3622 }
3623
3624 for (String ocName : objectClasses.values())
3625 {
3626 if (ocName.equalsIgnoreCase(OC_ALIAS))
3627 {
3628 return true;
3629 }
3630 }
3631
3632 return false;
3633 }
3634
3635 if (! objectClasses.containsKey(aliasOC))
3636 {
3637 return false;
3638 }
3639
3640 AttributeType aliasType =
3641 DirectoryServer.getAttributeType(ATTR_ALIAS_DN);
3642 if (aliasType == null)
3643 {
3644 // This should not happen -- The server doesn't have an
3645 // aliasedObjectName attribute type defined.
3646 if (debugEnabled())
3647 {
3648 TRACER.debugWarning(
3649 "No %s attribute type is defined in the server schema.",
3650 ATTR_ALIAS_DN);
3651 }
3652 return false;
3653 }
3654
3655 return (userAttributes.containsKey(aliasType) ||
3656 operationalAttributes.containsKey(aliasType));
3657 }
3658
3659
3660
3661 /**
3662 * Retrieves the DN of the entry referenced by this alias entry.
3663 * This should only be called if <CODE>isAlias()</CODE> returns
3664 * <CODE>true</CODE>.
3665 *
3666 * @return The DN of the entry referenced by this alias entry, or
3667 * <CODE>null</CODE> if it is not an alias.
3668 *
3669 * @throws DirectoryException If there is an aliasedObjectName
3670 * attribute but its value cannot be
3671 * parsed as a DN.
3672 */
3673 public DN getAliasedDN()
3674 throws DirectoryException
3675 {
3676 AttributeType aliasType =
3677 DirectoryServer.getAttributeType(ATTR_REFERRAL_URL);
3678 if (aliasType == null)
3679 {
3680 // This should not happen -- The server doesn't have an
3681 // aliasedObjectName attribute type defined.
3682 if (debugEnabled())
3683 {
3684 TRACER.debugWarning(
3685 "No %s attribute type is defined in the server schema.",
3686 ATTR_ALIAS_DN);
3687 }
3688 return null;
3689 }
3690
3691 List<Attribute> aliasAttrs = userAttributes.get(aliasType);
3692 if (aliasAttrs == null)
3693 {
3694 aliasAttrs = operationalAttributes.get(aliasType);
3695 if (aliasAttrs == null)
3696 {
3697 return null;
3698 }
3699 }
3700
3701 if (aliasAttrs.isEmpty())
3702 {
3703 return null;
3704 }
3705 else
3706 {
3707 // There should only be a single alias attribute in an entry,
3708 // and we'll skip the check for others for performance reasons.
3709 // We would just end up taking the first one anyway. The same
3710 // is true with the set of values, since it should be a
3711 // single-valued attribute.
3712 Attribute aliasAttr = aliasAttrs.get(0);
3713 LinkedHashSet<AttributeValue> attrValues =
3714 aliasAttr.getValues();
3715 if (attrValues.isEmpty())
3716 {
3717 return null;
3718 }
3719 else
3720 {
3721 return
3722 DN.decode(attrValues.iterator().next().getStringValue());
3723 }
3724 }
3725 }
3726
3727
3728
3729 /**
3730 * Indicates whether this entry meets the criteria to consider it an
3731 * LDAP subentry (i.e., it contains the "ldapSubentry" objectclass).
3732 *
3733 * @return <CODE>true</CODE> if this entry meets the criteria to
3734 * consider it an LDAP subentry, or <CODE>false</CODE> if
3735 * not.
3736 */
3737 public boolean isLDAPSubentry()
3738 {
3739 ObjectClass ldapSubentryOC =
3740 DirectoryServer.getObjectClass(OC_LDAP_SUBENTRY_LC);
3741 if (ldapSubentryOC == null)
3742 {
3743 // This should not happen -- The server doesn't have an
3744 // ldapsubentry objectclass defined.
3745 if (debugEnabled())
3746 {
3747 TRACER.debugWarning(
3748 "No %s objectclass is defined in the server schema.",
3749 OC_LDAP_SUBENTRY);
3750 }
3751
3752 for (String ocName : objectClasses.values())
3753 {
3754 if (ocName.equalsIgnoreCase(OC_LDAP_SUBENTRY))
3755 {
3756 return true;
3757 }
3758 }
3759
3760 return false;
3761 }
3762
3763
3764 // Make the determination based on whether this entry has the
3765 // ldapSubentry objectclass.
3766 return objectClasses.containsKey(ldapSubentryOC);
3767 }
3768
3769
3770
3771 /**
3772 * Indicates whether this entry falls within the range of the
3773 * provided search base DN and scope.
3774 *
3775 * @param baseDN The base DN for which to make the determination.
3776 * @param scope The search scope for which to make the
3777 * determination.
3778 *
3779 * @return <CODE>true</CODE> if this entry is within the given
3780 * base and scope, or <CODE>false</CODE> if it is not.
3781 */
3782 public boolean matchesBaseAndScope(DN baseDN, SearchScope scope)
3783 {
3784 return dn.matchesBaseAndScope(baseDN, scope);
3785 }
3786
3787
3788
3789 /**
3790 * Performs any necessary virtual attribute processing for this
3791 * entry. This should only be called at the time the entry is
3792 * decoded or created within the backend.
3793 */
3794 public void processVirtualAttributes()
3795 {
3796 processVirtualAttributes(true);
3797 }
3798
3799
3800
3801 /**
3802 * Performs any necessary virtual attribute processing for this
3803 * entry. This should only be called at the time the entry is
3804 * decoded or created within the backend.
3805 *
3806 * @param includeOperational Indicates whether to include
3807 * operational attributes.
3808 */
3809 public void processVirtualAttributes(boolean includeOperational)
3810 {
3811 for (VirtualAttributeRule rule :
3812 DirectoryServer.getVirtualAttributes(this))
3813 {
3814 AttributeType attributeType = rule.getAttributeType();
3815 if (attributeType.isOperational() && (! includeOperational))
3816 {
3817 continue;
3818 }
3819
3820 List<Attribute> attrList = userAttributes.get(attributeType);
3821 if ((attrList == null) || attrList.isEmpty())
3822 {
3823 attrList = operationalAttributes.get(attributeType);
3824 if ((attrList == null) || attrList.isEmpty())
3825 {
3826 // There aren't any conflicts, so we can just add the
3827 // attribute to the entry.
3828 attrList = new LinkedList<Attribute>();
3829 attrList.add(new VirtualAttribute(attributeType, this,
3830 rule));
3831 if (attributeType.isOperational())
3832 {
3833 operationalAttributes.put(attributeType, attrList);
3834 }
3835 else
3836 {
3837 userAttributes.put(attributeType, attrList);
3838 }
3839 }
3840 else
3841 {
3842 // There is a conflict with an existing operational
3843 // attribute.
3844 if (attrList.get(0).isVirtual())
3845 {
3846 // The existing attribute is already virtual, so we've got
3847 // a different conflict, but we'll let the first win.
3848 // FIXME -- Should we handle this differently?
3849 continue;
3850 }
3851
3852 // The conflict is with a real attribute. See what the
3853 // conflict behavior is and figure out how to handle it.
3854 switch (rule.getConflictBehavior())
3855 {
3856 case REAL_OVERRIDES_VIRTUAL:
3857 // We don't need to update the entry because the real
3858 // attribute will take precedence.
3859 break;
3860
3861 case VIRTUAL_OVERRIDES_REAL:
3862 // We need to move the real attribute to the suppressed
3863 // list and replace it with the virtual attribute.
3864 suppressedAttributes.put(attributeType, attrList);
3865 attrList = new LinkedList<Attribute>();
3866 attrList.add(new VirtualAttribute(attributeType, this,
3867 rule));
3868 operationalAttributes.put(attributeType, attrList);
3869 break;
3870
3871 case MERGE_REAL_AND_VIRTUAL:
3872 // We need to add the virtual attribute to the list and
3873 // keep the existing real attribute(s).
3874 attrList.add(new VirtualAttribute(attributeType, this,
3875 rule));
3876 break;
3877 }
3878 }
3879 }
3880 else
3881 {
3882 // There is a conflict with an existing user attribute.
3883 if (attrList.get(0).isVirtual())
3884 {
3885 // The existing attribute is already virtual, so we've got
3886 // a different conflict, but we'll let the first win.
3887 // FIXME -- Should we handle this differently?
3888 continue;
3889 }
3890
3891 // The conflict is with a real attribute. See what the
3892 // conflict behavior is and figure out how to handle it.
3893 switch (rule.getConflictBehavior())
3894 {
3895 case REAL_OVERRIDES_VIRTUAL:
3896 // We don't need to update the entry because the real
3897 // attribute will take precedence.
3898 break;
3899
3900 case VIRTUAL_OVERRIDES_REAL:
3901 // We need to move the real attribute to the suppressed
3902 // list and replace it with the virtual attribute.
3903 suppressedAttributes.put(attributeType, attrList);
3904 attrList = new LinkedList<Attribute>();
3905 attrList.add(new VirtualAttribute(attributeType, this,
3906 rule));
3907 userAttributes.put(attributeType, attrList);
3908 break;
3909
3910 case MERGE_REAL_AND_VIRTUAL:
3911 // We need to add the virtual attribute to the list and
3912 // keep the existing real attribute(s).
3913 attrList.add(new VirtualAttribute(attributeType, this,
3914 rule));
3915 break;
3916 }
3917 }
3918 }
3919
3920 virtualAttributeProcessingPerformed = true;
3921 }
3922
3923
3924
3925 /**
3926 * Indicates whether virtual attribute processing has been performed
3927 * for this entry.
3928 *
3929 * @return {@code true} if virtual attribute processing has been
3930 * performed for this entry, or {@code false} if not.
3931 */
3932 public boolean virtualAttributeProcessingPerformed()
3933 {
3934 return virtualAttributeProcessingPerformed;
3935 }
3936
3937
3938
3939 /**
3940 * Strips out all real attributes from this entry so that it only
3941 * contains virtual attributes.
3942 */
3943 public void stripRealAttributes()
3944 {
3945 // The objectClass attribute will always be a real attribute.
3946 objectClasses.clear();
3947
3948 Iterator<Map.Entry<AttributeType,List<Attribute>>>
3949 attrListIterator = userAttributes.entrySet().iterator();
3950 while (attrListIterator.hasNext())
3951 {
3952 Map.Entry<AttributeType,List<Attribute>> mapEntry =
3953 attrListIterator.next();
3954 Iterator<Attribute> attrIterator =
3955 mapEntry.getValue().iterator();
3956 while (attrIterator.hasNext())
3957 {
3958 Attribute a = attrIterator.next();
3959 if (! a.isVirtual())
3960 {
3961 attrIterator.remove();
3962 }
3963 }
3964
3965 if (mapEntry.getValue().isEmpty())
3966 {
3967 attrListIterator.remove();
3968 }
3969 }
3970
3971 attrListIterator = operationalAttributes.entrySet().iterator();
3972 while (attrListIterator.hasNext())
3973 {
3974 Map.Entry<AttributeType,List<Attribute>> mapEntry =
3975 attrListIterator.next();
3976 Iterator<Attribute> attrIterator =
3977 mapEntry.getValue().iterator();
3978 while (attrIterator.hasNext())
3979 {
3980 Attribute a = attrIterator.next();
3981 if (! a.isVirtual())
3982 {
3983 attrIterator.remove();
3984 }
3985 }
3986
3987 if (mapEntry.getValue().isEmpty())
3988 {
3989 attrListIterator.remove();
3990 }
3991 }
3992 }
3993
3994
3995
3996 /**
3997 * Strips out all virtual attributes from this entry so that it only
3998 * contains real attributes.
3999 */
4000 public void stripVirtualAttributes()
4001 {
4002 Iterator<Map.Entry<AttributeType,List<Attribute>>>
4003 attrListIterator = userAttributes.entrySet().iterator();
4004 while (attrListIterator.hasNext())
4005 {
4006 Map.Entry<AttributeType,List<Attribute>> mapEntry =
4007 attrListIterator.next();
4008 Iterator<Attribute> attrIterator =
4009 mapEntry.getValue().iterator();
4010 while (attrIterator.hasNext())
4011 {
4012 Attribute a = attrIterator.next();
4013 if (a.isVirtual())
4014 {
4015 attrIterator.remove();
4016 }
4017 }
4018
4019 if (mapEntry.getValue().isEmpty())
4020 {
4021 attrListIterator.remove();
4022 }
4023 }
4024
4025 attrListIterator = operationalAttributes.entrySet().iterator();
4026 while (attrListIterator.hasNext())
4027 {
4028 Map.Entry<AttributeType,List<Attribute>> mapEntry =
4029 attrListIterator.next();
4030 Iterator<Attribute> attrIterator =
4031 mapEntry.getValue().iterator();
4032 while (attrIterator.hasNext())
4033 {
4034 Attribute a = attrIterator.next();
4035 if (a.isVirtual())
4036 {
4037 attrIterator.remove();
4038 }
4039 }
4040
4041 if (mapEntry.getValue().isEmpty())
4042 {
4043 attrListIterator.remove();
4044 }
4045 }
4046 }
4047
4048
4049
4050 /**
4051 * Encodes this entry into a form that is suitable for long-term
4052 * persistent storage. The encoding will have a version number so
4053 * that if the way we store entries changes in the future we will
4054 * still be able to read entries encoded in an older format.
4055 *
4056 * @param config The configuration that may be used to control how
4057 * the entry is encoded.
4058 *
4059 * @return The entry encoded in a form that is suitable for
4060 * long-term persistent storage.
4061 *
4062 * @throws DirectoryException If a problem occurs while attempting
4063 * to encode the entry.
4064 */
4065 public byte[] encode(EntryEncodeConfig config)
4066 throws DirectoryException
4067 {
4068 return encodeV2(config);
4069 }
4070
4071
4072
4073 /**
4074 * Encodes this entry using the V1 encoding.
4075 *
4076 * @return The entry encoded in the V1 encoding.
4077 */
4078 public byte[] encodeV1()
4079 {
4080 // The version number will be one byte. We'll add that later.
4081
4082
4083 // The DN will be encoded as a one-to-five byte length followed
4084 // byte the UTF-8 byte representation.
4085 byte[] dnBytes = getBytes(dn.toString());
4086 byte[] dnLength = ASN1Element.encodeLength(dnBytes.length);
4087 int totalBytes = 1 + dnBytes.length + dnLength.length;
4088
4089
4090 // The object classes will be encoded as one-to-five byte length
4091 // followed by a zero-delimited UTF-8 byte representation of the
4092 // names (e.g., top\0person\0organizationalPerson\0inetOrgPerson).
4093 int i=0;
4094 int totalOCBytes = objectClasses.size() - 1;
4095 byte[][] ocBytes = new byte[objectClasses.size()][];
4096 for (String ocName : objectClasses.values())
4097 {
4098 ocBytes[i] = getBytes(ocName);
4099 totalOCBytes += ocBytes[i++].length;
4100 }
4101 byte[] ocLength = ASN1Element.encodeLength(totalOCBytes);
4102 totalBytes += totalOCBytes + ocLength.length;
4103
4104
4105 // The user attributes will be encoded as a one-to-five byte
4106 // number of attributes followed by a sequence of:
4107 // - A UTF-8 byte representation of the attribute name.
4108 // - A zero delimiter
4109 // - A one-to-five byte number of values for the attribute
4110 // - A sequence of:
4111 // - A one-to-five byte length for the value
4112 // - A UTF-8 byte representation for the value
4113 i=0;
4114 int numUserAttributes = 0;
4115 int totalUserAttrBytes = 0;
4116 LinkedList<byte[]> userAttrBytes = new LinkedList<byte[]>();
4117 for (List<Attribute> attrList : userAttributes.values())
4118 {
4119 for (Attribute a : attrList)
4120 {
4121 if (a.isVirtual() || (! a.hasValue()))
4122 {
4123 continue;
4124 }
4125
4126 numUserAttributes++;
4127
4128 byte[] nameBytes = getBytes(a.getNameWithOptions());
4129
4130 int numValues = 0;
4131 int totalValueBytes = 0;
4132 LinkedList<byte[]> valueBytes = new LinkedList<byte[]>();
4133 for (AttributeValue v : a.getValues())
4134 {
4135 numValues++;
4136 byte[] vBytes = v.getValueBytes();
4137 byte[] vLength = ASN1Element.encodeLength(vBytes.length);
4138 valueBytes.add(vLength);
4139 valueBytes.add(vBytes);
4140 totalValueBytes += vLength.length + vBytes.length;
4141 }
4142 byte[] numValuesBytes = ASN1Element.encodeLength(numValues);
4143
4144 byte[] attrBytes = new byte[nameBytes.length +
4145 numValuesBytes.length +
4146 totalValueBytes + 1];
4147 System.arraycopy(nameBytes, 0, attrBytes, 0,
4148 nameBytes.length);
4149
4150 int pos = nameBytes.length+1;
4151 System.arraycopy(numValuesBytes, 0, attrBytes, pos,
4152 numValuesBytes.length);
4153 pos += numValuesBytes.length;
4154 for (byte[] b : valueBytes)
4155 {
4156 System.arraycopy(b, 0, attrBytes, pos, b.length);
4157 pos += b.length;
4158 }
4159
4160 userAttrBytes.add(attrBytes);
4161 totalUserAttrBytes += attrBytes.length;
4162 }
4163 }
4164 byte[] userAttrCount =
4165 ASN1OctetString.encodeLength(numUserAttributes);
4166 totalBytes += totalUserAttrBytes + userAttrCount.length;
4167
4168
4169 // The operational attributes will be encoded in the same way as
4170 // the user attributes.
4171 i=0;
4172 int numOperationalAttributes = 0;
4173 int totalOperationalAttrBytes = 0;
4174 LinkedList<byte[]> operationalAttrBytes =
4175 new LinkedList<byte[]>();
4176 for (List<Attribute> attrList : operationalAttributes.values())
4177 {
4178 for (Attribute a : attrList)
4179 {
4180 if (a.isVirtual() || (! a.hasValue()))
4181 {
4182 continue;
4183 }
4184
4185 numOperationalAttributes++;
4186
4187 byte[] nameBytes = getBytes(a.getNameWithOptions());
4188
4189 int numValues = 0;
4190 int totalValueBytes = 0;
4191 LinkedList<byte[]> valueBytes = new LinkedList<byte[]>();
4192 for (AttributeValue v : a.getValues())
4193 {
4194 numValues++;
4195 byte[] vBytes = v.getValueBytes();
4196 byte[] vLength = ASN1Element.encodeLength(vBytes.length);
4197 valueBytes.add(vLength);
4198 valueBytes.add(vBytes);
4199 totalValueBytes += vLength.length + vBytes.length;
4200 }
4201 byte[] numValuesBytes = ASN1Element.encodeLength(numValues);
4202
4203 byte[] attrBytes = new byte[nameBytes.length +
4204 numValuesBytes.length +
4205 totalValueBytes + 1];
4206 System.arraycopy(nameBytes, 0, attrBytes, 0,
4207 nameBytes.length);
4208
4209 int pos = nameBytes.length+1;
4210 System.arraycopy(numValuesBytes, 0, attrBytes, pos,
4211 numValuesBytes.length);
4212 pos += numValuesBytes.length;
4213 for (byte[] b : valueBytes)
4214 {
4215 System.arraycopy(b, 0, attrBytes, pos, b.length);
4216 pos += b.length;
4217 }
4218
4219 operationalAttrBytes.add(attrBytes);
4220 totalOperationalAttrBytes += attrBytes.length;
4221 }
4222 }
4223 byte[] operationalAttrCount =
4224 ASN1OctetString.encodeLength(numOperationalAttributes);
4225 totalBytes += totalOperationalAttrBytes +
4226 operationalAttrCount.length;
4227
4228
4229 // Now we've got all the data that we need. Create a big byte
4230 // array to hold it all and pack it in.
4231 byte[] entryBytes = new byte[totalBytes];
4232
4233
4234 // Add the entry version number as the first byte.
4235 entryBytes[0] = 0x01;
4236
4237
4238 // Next, add the DN length and value.
4239 System.arraycopy(dnLength, 0, entryBytes, 1, dnLength.length);
4240 int pos = 1 + dnLength.length;
4241 System.arraycopy(dnBytes, 0, entryBytes, pos, dnBytes.length);
4242 pos += dnBytes.length;
4243
4244
4245 // Next, add the object classes length and values.
4246 System.arraycopy(ocLength, 0, entryBytes, pos, ocLength.length);
4247 pos += ocLength.length;
4248 for (byte[] b : ocBytes)
4249 {
4250 System.arraycopy(b, 0, entryBytes, pos, b.length);
4251 pos += b.length + 1;
4252 }
4253
4254 // We need to back up one because there's no zero-teriminator
4255 // after the last object class name.
4256 pos--;
4257
4258
4259 // Next, add the user attribute count and the user attribute
4260 // data.
4261 System.arraycopy(userAttrCount, 0, entryBytes, pos,
4262 userAttrCount.length);
4263 pos += userAttrCount.length;
4264 for (byte[] b : userAttrBytes)
4265 {
4266 System.arraycopy(b, 0, entryBytes, pos, b.length);
4267 pos += b.length;
4268 }
4269
4270
4271 // Finally, add the operational attribute count and the
4272 // operational attribute data.
4273 System.arraycopy(operationalAttrCount, 0, entryBytes, pos,
4274 operationalAttrCount.length);
4275 pos += operationalAttrCount.length;
4276 for (byte[] b : operationalAttrBytes)
4277 {
4278 System.arraycopy(b, 0, entryBytes, pos, b.length);
4279 pos += b.length;
4280 }
4281
4282 return entryBytes;
4283 }
4284
4285
4286
4287 /**
4288 * Encodes this entry using the V2 encoding.
4289 *
4290 * @param config The configuration that should be used to encode
4291 * the entry.
4292 *
4293 * @return The entry encoded in the V2 encoding.
4294 *
4295 * @throws DirectoryException If a problem occurs while attempting
4296 * to encode the entry.
4297 */
4298 public byte[] encodeV2(EntryEncodeConfig config)
4299 throws DirectoryException
4300 {
4301 // The version number will be one byte. We'll add that later.
4302
4303
4304 // Get the encoded respresentation of the config.
4305 byte[] configBytes = config.encode();
4306 byte[] configLength =
4307 ASN1Element.encodeLength(configBytes.length);
4308 int totalBytes = 1 + configBytes.length + configLength.length;
4309
4310
4311 // If we should include the DN, then it will be encoded as a
4312 // one-to-five byte length followed by the UTF-8 byte
4313 // representation.
4314 byte[] dnBytes = null;
4315 byte[] dnLength = null;
4316 if (! config.excludeDN())
4317 {
4318 dnBytes = getBytes(dn.toString());
4319 dnLength = ASN1Element.encodeLength(dnBytes.length);
4320 totalBytes += dnBytes.length + dnLength.length;
4321 }
4322
4323
4324 // Encode the object classes in the appropriate manner.
4325 byte[] ocLength;
4326 LinkedList<byte[]> ocBytes = new LinkedList<byte[]>();
4327 if (config.compressObjectClassSets())
4328 {
4329 byte[] b = config.getCompressedSchema().
4330 encodeObjectClasses(objectClasses);
4331 ocBytes.add(b);
4332 ocLength = ASN1Element.encodeLength(b.length);
4333 totalBytes += ocLength.length + b.length;
4334 }
4335 else
4336 {
4337 int totalOCBytes = objectClasses.size() - 1;
4338 for (String ocName : objectClasses.values())
4339 {
4340 byte[] b = getBytes(ocName);
4341 ocBytes.add(b);
4342 totalOCBytes += b.length;
4343 }
4344 ocLength = ASN1Element.encodeLength(totalOCBytes);
4345 totalBytes += totalOCBytes + ocLength.length;
4346 }
4347
4348
4349 // Encode the user attributes in the appropriate manner.
4350 int numUserAttributes = 0;
4351 int totalUserAttrBytes = 0;
4352 LinkedList<byte[]> userAttrBytes = new LinkedList<byte[]>();
4353 if (config.compressAttributeDescriptions())
4354 {
4355 for (List<Attribute> attrList : userAttributes.values())
4356 {
4357 for (Attribute a : attrList)
4358 {
4359 if (a.isVirtual() || (! a.hasValue()))
4360 {
4361 continue;
4362 }
4363
4364 numUserAttributes++;
4365
4366 byte[] attrBytes =
4367 config.getCompressedSchema().encodeAttribute(a);
4368 byte[] lengthBytes =
4369 ASN1Element.encodeLength(attrBytes.length);
4370 userAttrBytes.add(lengthBytes);
4371 userAttrBytes.add(attrBytes);
4372 totalUserAttrBytes += lengthBytes.length + attrBytes.length;
4373 }
4374 }
4375 }
4376 else
4377 {
4378 // The user attributes will be encoded as a one-to-five byte
4379 // number of attributes followed by a sequence of:
4380 // - A UTF-8 byte representation of the attribute name.
4381 // - A zero delimiter
4382 // - A one-to-five byte number of values for the attribute
4383 // - A sequence of:
4384 // - A one-to-five byte length for the value
4385 // - A UTF-8 byte representation for the value
4386 for (List<Attribute> attrList : userAttributes.values())
4387 {
4388 for (Attribute a : attrList)
4389 {
4390 if (a.isVirtual() || (! a.hasValue()))
4391 {
4392 continue;
4393 }
4394
4395 numUserAttributes++;
4396
4397 byte[] nameBytes = getBytes(a.getNameWithOptions());
4398
4399 int numValues = 0;
4400 int totalValueBytes = 0;
4401 LinkedList<byte[]> valueBytes = new LinkedList<byte[]>();
4402 for (AttributeValue v : a.getValues())
4403 {
4404 numValues++;
4405 byte[] vBytes = v.getValueBytes();
4406 byte[] vLength = ASN1Element.encodeLength(vBytes.length);
4407 valueBytes.add(vLength);
4408 valueBytes.add(vBytes);
4409 totalValueBytes += vLength.length + vBytes.length;
4410 }
4411 byte[] numValuesBytes = ASN1Element.encodeLength(numValues);
4412
4413 byte[] attrBytes = new byte[nameBytes.length +
4414 numValuesBytes.length +
4415 totalValueBytes + 1];
4416 System.arraycopy(nameBytes, 0, attrBytes, 0,
4417 nameBytes.length);
4418
4419 int pos = nameBytes.length+1;
4420 System.arraycopy(numValuesBytes, 0, attrBytes, pos,
4421 numValuesBytes.length);
4422 pos += numValuesBytes.length;
4423 for (byte[] b : valueBytes)
4424 {
4425 System.arraycopy(b, 0, attrBytes, pos, b.length);
4426 pos += b.length;
4427 }
4428
4429 userAttrBytes.add(attrBytes);
4430 totalUserAttrBytes += attrBytes.length;
4431 }
4432 }
4433 }
4434 byte[] userAttrCount =
4435 ASN1OctetString.encodeLength(numUserAttributes);
4436 totalBytes += totalUserAttrBytes + userAttrCount.length;
4437
4438
4439 // Encode the operational attributes in the appropriate manner.
4440 int numOperationalAttributes = 0;
4441 int totalOperationalAttrBytes = 0;
4442 LinkedList<byte[]> operationalAttrBytes =
4443 new LinkedList<byte[]>();
4444 if (config.compressAttributeDescriptions())
4445 {
4446 for (List<Attribute> attrList : operationalAttributes.values())
4447 {
4448 for (Attribute a : attrList)
4449 {
4450 if (a.isVirtual() || (! a.hasValue()))
4451 {
4452 continue;
4453 }
4454
4455 numOperationalAttributes++;
4456
4457 byte[] attrBytes =
4458 config.getCompressedSchema().encodeAttribute(a);
4459 byte[] lengthBytes =
4460 ASN1Element.encodeLength(attrBytes.length);
4461 operationalAttrBytes.add(lengthBytes);
4462 operationalAttrBytes.add(attrBytes);
4463 totalOperationalAttrBytes +=
4464 lengthBytes.length + attrBytes.length;
4465 }
4466 }
4467 }
4468 else
4469 {
4470 // Encode the operational attributes in the same way as the user
4471 // attributes.
4472 for (List<Attribute> attrList : operationalAttributes.values())
4473 {
4474 for (Attribute a : attrList)
4475 {
4476 if (a.isVirtual() || (! a.hasValue()))
4477 {
4478 continue;
4479 }
4480
4481 numOperationalAttributes++;
4482
4483 byte[] nameBytes = getBytes(a.getNameWithOptions());
4484
4485 int numValues = 0;
4486 int totalValueBytes = 0;
4487 LinkedList<byte[]> valueBytes = new LinkedList<byte[]>();
4488 for (AttributeValue v : a.getValues())
4489 {
4490 numValues++;
4491 byte[] vBytes = v.getValueBytes();
4492 byte[] vLength = ASN1Element.encodeLength(vBytes.length);
4493 valueBytes.add(vLength);
4494 valueBytes.add(vBytes);
4495 totalValueBytes += vLength.length + vBytes.length;
4496 }
4497 byte[] numValuesBytes = ASN1Element.encodeLength(numValues);
4498
4499 byte[] attrBytes = new byte[nameBytes.length +
4500 numValuesBytes.length +
4501 totalValueBytes + 1];
4502 System.arraycopy(nameBytes, 0, attrBytes, 0,
4503 nameBytes.length);
4504
4505 int pos = nameBytes.length+1;
4506 System.arraycopy(numValuesBytes, 0, attrBytes, pos,
4507 numValuesBytes.length);
4508 pos += numValuesBytes.length;
4509 for (byte[] b : valueBytes)
4510 {
4511 System.arraycopy(b, 0, attrBytes, pos, b.length);
4512 pos += b.length;
4513 }
4514
4515 operationalAttrBytes.add(attrBytes);
4516 totalOperationalAttrBytes += attrBytes.length;
4517 }
4518 }
4519 }
4520 byte[] operationalAttrCount =
4521 ASN1OctetString.encodeLength(numOperationalAttributes);
4522 totalBytes += totalOperationalAttrBytes +
4523 operationalAttrCount.length;
4524
4525
4526 // Now we've got all the data that we need. Create a big byte
4527 // array to hold it all and pack it in.
4528 byte[] entryBytes = new byte[totalBytes];
4529
4530
4531 // Add the entry version number as the first byte.
4532 entryBytes[0] = 0x02;
4533
4534
4535 // Next, add the encoded config.
4536 System.arraycopy(configLength, 0, entryBytes, 1,
4537 configLength.length);
4538 int pos = 1 + configLength.length;
4539 System.arraycopy(configBytes, 0, entryBytes, pos,
4540 configBytes.length);
4541 pos += configBytes.length;
4542
4543
4544 // Next, add the DN length and value.
4545 if (! config.excludeDN())
4546 {
4547 System.arraycopy(dnLength, 0, entryBytes, pos, dnLength.length);
4548 pos += dnLength.length;
4549 System.arraycopy(dnBytes, 0, entryBytes, pos, dnBytes.length);
4550 pos += dnBytes.length;
4551 }
4552
4553
4554 // Next, add the object classes length and values.
4555 System.arraycopy(ocLength, 0, entryBytes, pos, ocLength.length);
4556 pos += ocLength.length;
4557 if (config.compressObjectClassSets())
4558 {
4559 for (byte[] b : ocBytes)
4560 {
4561 System.arraycopy(b, 0, entryBytes, pos, b.length);
4562 pos += b.length;
4563 }
4564 }
4565 else
4566 {
4567 for (byte[] b : ocBytes)
4568 {
4569 System.arraycopy(b, 0, entryBytes, pos, b.length);
4570 pos += b.length + 1;
4571 }
4572
4573 // We need to back up one because there's no zero-teriminator
4574 // after the last object class name.
4575 pos--;
4576 }
4577
4578
4579 // Next, add the user attribute count and the user attribute
4580 // data.
4581 System.arraycopy(userAttrCount, 0, entryBytes, pos,
4582 userAttrCount.length);
4583 pos += userAttrCount.length;
4584 for (byte[] b : userAttrBytes)
4585 {
4586 System.arraycopy(b, 0, entryBytes, pos, b.length);
4587 pos += b.length;
4588 }
4589
4590
4591 // Finally, add the operational attribute count and the
4592 // operational attribute data.
4593 System.arraycopy(operationalAttrCount, 0, entryBytes, pos,
4594 operationalAttrCount.length);
4595 pos += operationalAttrCount.length;
4596 for (byte[] b : operationalAttrBytes)
4597 {
4598 System.arraycopy(b, 0, entryBytes, pos, b.length);
4599 pos += b.length;
4600 }
4601
4602 return entryBytes;
4603 }
4604
4605
4606
4607 /**
4608 * Decodes the provided byte array as an entry.
4609 *
4610 * @param entryBytes The byte array containing the data to be
4611 * decoded.
4612 *
4613 * @return The decoded entry.
4614 *
4615 * @throws DirectoryException If the provided byte array cannot be
4616 * decoded as an entry.
4617 */
4618 public static Entry decode(byte[] entryBytes)
4619 throws DirectoryException
4620 {
4621 return decode(entryBytes,
4622 DirectoryServer.getDefaultCompressedSchema());
4623 }
4624
4625
4626
4627 /**
4628 * Decodes the provided byte array as an entry.
4629 *
4630 * @param entryBytes The byte array containing the data to
4631 * be decoded.
4632 * @param compressedSchema The compressed schema manager to use
4633 * when decoding tokenized schema
4634 * elements.
4635 *
4636 * @return The decoded entry.
4637 *
4638 * @throws DirectoryException If the provided byte array cannot be
4639 * decoded as an entry.
4640 */
4641 public static Entry decode(byte[] entryBytes,
4642 CompressedSchema compressedSchema)
4643 throws DirectoryException
4644 {
4645 switch(entryBytes[0])
4646 {
4647 case 0x01:
4648 return decodeV1(entryBytes);
4649 case 0x02:
4650 return decodeV2(entryBytes, compressedSchema);
4651 default:
4652 Message message = ERR_ENTRY_DECODE_UNRECOGNIZED_VERSION.get(
4653 byteToHex(entryBytes[0]));
4654 throw new DirectoryException(
4655 DirectoryServer.getServerErrorResultCode(),
4656 message);
4657 }
4658 }
4659
4660
4661
4662 /**
4663 * Decodes the provided byte array as an entry using the V1
4664 * encoding.
4665 *
4666 * @param entryBytes The byte array containing the data to be
4667 * decoded.
4668 *
4669 * @return The decoded entry.
4670 *
4671 * @throws DirectoryException If the provided byte array cannot be
4672 * decoded as an entry.
4673 */
4674 public static Entry decodeV1(byte[] entryBytes)
4675 throws DirectoryException
4676 {
4677 try
4678 {
4679 // The first byte must be the entry version. If it's not one
4680 // we recognize, then that's an error.
4681 if (entryBytes[0] != 0x01)
4682 {
4683 Message message = ERR_ENTRY_DECODE_UNRECOGNIZED_VERSION.get(
4684 byteToHex(entryBytes[0]));
4685 throw new DirectoryException(
4686 DirectoryServer.getServerErrorResultCode(),
4687 message);
4688 }
4689
4690
4691 // Next is the length of the DN. It may be a single byte or
4692 // multiple bytes.
4693 int pos = 1;
4694 int dnLength = entryBytes[pos] & 0x7F;
4695 if (entryBytes[pos++] != dnLength)
4696 {
4697 int numLengthBytes = dnLength;
4698 dnLength = 0;
4699 for (int i=0; i < numLengthBytes; i++, pos++)
4700 {
4701 dnLength = (dnLength << 8) | (entryBytes[pos] & 0xFF);
4702 }
4703 }
4704
4705
4706 // Next is the DN itself.
4707 byte[] dnBytes = new byte[dnLength];
4708 System.arraycopy(entryBytes, pos, dnBytes, 0, dnLength);
4709 pos += dnLength;
4710 DN dn = DN.decode(new ASN1OctetString(dnBytes));
4711
4712
4713 // Next is the length of the object classes. It may be a single
4714 // byte or multiple bytes.
4715 int ocLength = entryBytes[pos] & 0x7F;
4716 if (entryBytes[pos++] != ocLength)
4717 {
4718 int numLengthBytes = ocLength;
4719 ocLength = 0;
4720 for (int i=0; i < numLengthBytes; i++, pos++)
4721 {
4722 ocLength = (ocLength << 8) | (entryBytes[pos] & 0xFF);
4723 }
4724 }
4725
4726
4727 // Next is the encoded set of object classes. It will be a
4728 // single string with the object class names separated by zeros.
4729 LinkedHashMap<ObjectClass,String> objectClasses =
4730 new LinkedHashMap<ObjectClass,String>();
4731 int startPos = pos;
4732 for (int i=0; i < ocLength; i++,pos++)
4733 {
4734 if (entryBytes[pos] == 0x00)
4735 {
4736 String name = new String(entryBytes, startPos, pos-startPos,
4737 "UTF-8");
4738 String lowerName = toLowerCase(name);
4739 ObjectClass oc =
4740 DirectoryServer.getObjectClass(lowerName, true);
4741 objectClasses.put(oc, name);
4742 startPos = pos+1;
4743 }
4744 }
4745 String name = new String(entryBytes, startPos, pos-startPos,
4746 "UTF-8");
4747 String lowerName = toLowerCase(name);
4748 ObjectClass oc =
4749 DirectoryServer.getObjectClass(lowerName, true);
4750 objectClasses.put(oc, name);
4751
4752
4753 // Next is the total number of user attributes. It may be a
4754 // single byte or multiple bytes.
4755 int numUserAttrs = entryBytes[pos] & 0x7F;
4756 if (entryBytes[pos++] != numUserAttrs)
4757 {
4758 int numLengthBytes = numUserAttrs;
4759 numUserAttrs = 0;
4760 for (int i=0; i < numLengthBytes; i++, pos++)
4761 {
4762 numUserAttrs = (numUserAttrs << 8) |
4763 (entryBytes[pos] & 0xFF);
4764 }
4765 }
4766
4767
4768 // Now, we should iterate through the user attributes and decode
4769 // each one.
4770 LinkedHashMap<AttributeType,List<Attribute>> userAttributes =
4771 new LinkedHashMap<AttributeType,List<Attribute>>();
4772 for (int i=0; i < numUserAttrs; i++)
4773 {
4774 // First, we have the zero-terminated attribute name.
4775 startPos = pos;
4776 while (entryBytes[pos] != 0x00)
4777 {
4778 pos++;
4779 }
4780 name = new String(entryBytes, startPos, pos-startPos,
4781 "UTF-8");
4782 LinkedHashSet<String> options;
4783 int semicolonPos = name.indexOf(';');
4784 if (semicolonPos > 0)
4785 {
4786 String baseName = name.substring(0, semicolonPos);
4787 lowerName = toLowerCase(baseName);
4788 options = new LinkedHashSet<String>();
4789
4790 int nextPos = name.indexOf(';', semicolonPos+1);
4791 while (nextPos > 0)
4792 {
4793 String option = name.substring(semicolonPos+1, nextPos);
4794 if (option.length() > 0)
4795 {
4796 options.add(option);
4797 }
4798
4799 semicolonPos = nextPos;
4800 nextPos = name.indexOf(';', semicolonPos+1);
4801 }
4802
4803 String option = name.substring(semicolonPos+1);
4804 if (option.length() > 0)
4805 {
4806 options.add(option);
4807 }
4808
4809 name = baseName;
4810 }
4811 else
4812 {
4813 lowerName = toLowerCase(name);
4814 options = new LinkedHashSet<String>(0);
4815 }
4816 AttributeType attributeType =
4817 DirectoryServer.getAttributeType(lowerName, true);
4818
4819
4820
4821 // Next, we have the number of values.
4822 int numValues = entryBytes[++pos] & 0x7F;
4823 if (entryBytes[pos++] != numValues)
4824 {
4825 int numLengthBytes = numValues;
4826 numValues = 0;
4827 for (int j=0; j < numLengthBytes; j++, pos++)
4828 {
4829 numValues = (numValues << 8) | (entryBytes[pos] & 0xFF);
4830 }
4831 }
4832
4833 // Next, we have the sequence of length-value pairs.
4834 LinkedHashSet<AttributeValue> values =
4835 new LinkedHashSet<AttributeValue>(numValues);
4836 for (int j=0; j < numValues; j++)
4837 {
4838 int valueLength = entryBytes[pos] & 0x7F;
4839 if (entryBytes[pos++] != valueLength)
4840 {
4841 int numLengthBytes = valueLength;
4842 valueLength = 0;
4843 for (int k=0; k < numLengthBytes; k++, pos++)
4844 {
4845 valueLength = (valueLength << 8) |
4846 (entryBytes[pos] & 0xFF);
4847 }
4848 }
4849
4850 byte[] valueBytes = new byte[valueLength];
4851 System.arraycopy(entryBytes, pos, valueBytes, 0,
4852 valueLength);
4853 values.add(new AttributeValue(attributeType,
4854 new ASN1OctetString(valueBytes)));
4855 pos += valueLength;
4856 }
4857
4858
4859 // Create the attribute and add it to the set of user
4860 // attributes.
4861 Attribute a = new Attribute(attributeType, name, options,
4862 values);
4863 List<Attribute> attrList = userAttributes.get(attributeType);
4864 if (attrList == null)
4865 {
4866 attrList = new ArrayList<Attribute>(1);
4867 attrList.add(a);
4868 userAttributes.put(attributeType, attrList);
4869 }
4870 else
4871 {
4872 attrList.add(a);
4873 }
4874 }
4875
4876
4877 // Next is the total number of operational attributes. It may
4878 // be a single byte or multiple bytes.
4879 int numOperationalAttrs = entryBytes[pos] & 0x7F;
4880 if (entryBytes[pos++] != numOperationalAttrs)
4881 {
4882 int numLengthBytes = numOperationalAttrs;
4883 numOperationalAttrs = 0;
4884 for (int i=0; i < numLengthBytes; i++, pos++)
4885 {
4886 numOperationalAttrs =
4887 (numOperationalAttrs << 8) | (entryBytes[pos] & 0xFF);
4888 }
4889 }
4890
4891
4892 // Now, we should iterate through the operational attributes and
4893 // decode each one.
4894 LinkedHashMap<AttributeType,List<Attribute>>
4895 operationalAttributes =
4896 new LinkedHashMap<AttributeType,List<Attribute>>();
4897 for (int i=0; i < numOperationalAttrs; i++)
4898 {
4899 // First, we have the zero-terminated attribute name.
4900 startPos = pos;
4901 while (entryBytes[pos] != 0x00)
4902 {
4903 pos++;
4904 }
4905 name = new String(entryBytes, startPos, pos-startPos,
4906 "UTF-8");
4907 LinkedHashSet<String> options;
4908 int semicolonPos = name.indexOf(';');
4909 if (semicolonPos > 0)
4910 {
4911 String baseName = name.substring(0, semicolonPos);
4912 lowerName = toLowerCase(baseName);
4913 options = new LinkedHashSet<String>();
4914
4915 int nextPos = name.indexOf(';', semicolonPos+1);
4916 while (nextPos > 0)
4917 {
4918 String option = name.substring(semicolonPos+1, nextPos);
4919 if (option.length() > 0)
4920 {
4921 options.add(option);
4922 }
4923
4924 semicolonPos = nextPos;
4925 nextPos = name.indexOf(';', semicolonPos+1);
4926 }
4927
4928 String option = name.substring(semicolonPos+1);
4929 if (option.length() > 0)
4930 {
4931 options.add(option);
4932 }
4933
4934 name = baseName;
4935 }
4936 else
4937 {
4938 lowerName = toLowerCase(name);
4939 options = new LinkedHashSet<String>(0);
4940 }
4941 AttributeType attributeType =
4942 DirectoryServer.getAttributeType(lowerName, true);
4943
4944
4945 // Next, we have the number of values.
4946 int numValues = entryBytes[++pos] & 0x7F;
4947 if (entryBytes[pos++] != numValues)
4948 {
4949 int numLengthBytes = numValues;
4950 numValues = 0;
4951 for (int j=0; j < numLengthBytes; j++, pos++)
4952 {
4953 numValues = (numValues << 8) | (entryBytes[pos] & 0xFF);
4954 }
4955 }
4956
4957 // Next, we have the sequence of length-value pairs.
4958 LinkedHashSet<AttributeValue> values =
4959 new LinkedHashSet<AttributeValue>(numValues);
4960 for (int j=0; j < numValues; j++)
4961 {
4962 int valueLength = entryBytes[pos] & 0x7F;
4963 if (entryBytes[pos++] != valueLength)
4964 {
4965 int numLengthBytes = valueLength;
4966 valueLength = 0;
4967 for (int k=0; k < numLengthBytes; k++, pos++)
4968 {
4969 valueLength = (valueLength << 8) |
4970 (entryBytes[pos] & 0xFF);
4971 }
4972 }
4973
4974 byte[] valueBytes = new byte[valueLength];
4975 System.arraycopy(entryBytes, pos, valueBytes, 0,
4976 valueLength);
4977 values.add(new AttributeValue(attributeType,
4978 new ASN1OctetString(valueBytes)));
4979 pos += valueLength;
4980 }
4981
4982
4983 // Create the attribute and add it to the set of operational
4984 // attributes.
4985 Attribute a = new Attribute(attributeType, name, options,
4986 values);
4987 List<Attribute> attrList =
4988 operationalAttributes.get(attributeType);
4989 if (attrList == null)
4990 {
4991 attrList = new ArrayList<Attribute>(1);
4992 attrList.add(a);
4993 operationalAttributes.put(attributeType, attrList);
4994 }
4995 else
4996 {
4997 attrList.add(a);
4998 }
4999 }
5000
5001
5002 // We've got everything that we need, so create and return the
5003 // entry.
5004 return new Entry(dn, objectClasses, userAttributes,
5005 operationalAttributes);
5006 }
5007 catch (DirectoryException de)
5008 {
5009 throw de;
5010 }
5011 catch (Exception e)
5012 {
5013 if (debugEnabled())
5014 {
5015 TRACER.debugCaught(DebugLogLevel.ERROR, e);
5016 }
5017
5018 Message message =
5019 ERR_ENTRY_DECODE_EXCEPTION.get(getExceptionMessage(e));
5020 throw new DirectoryException(
5021 DirectoryServer.getServerErrorResultCode(),
5022 message, e);
5023 }
5024 }
5025
5026
5027
5028 /**
5029 * Decodes the provided byte array as an entry using the V2
5030 * encoding.
5031 *
5032 * @param entryBytes The byte array containing the data to
5033 * be decoded.
5034 * @param compressedSchema The compressed schema manager to use
5035 * when decoding tokenized schema
5036 * elements.
5037 *
5038 * @return The decoded entry.
5039 *
5040 * @throws DirectoryException If the provided byte array cannot be
5041 * decoded as an entry.
5042 */
5043 public static Entry decodeV2(byte[] entryBytes,
5044 CompressedSchema compressedSchema)
5045 throws DirectoryException
5046 {
5047 try
5048 {
5049 // The first byte must be the entry version. If it's not one
5050 // we recognize, then that's an error.
5051 if (entryBytes[0] != 0x02)
5052 {
5053 Message message = ERR_ENTRY_DECODE_UNRECOGNIZED_VERSION.get(
5054 byteToHex(entryBytes[0]));
5055 throw new DirectoryException(
5056 DirectoryServer.getServerErrorResultCode(),
5057 message);
5058 }
5059
5060
5061 // Next is the length of the encoded configuration. It may be a
5062 // single byte or multiple bytes.
5063 int pos = 1;
5064 int configLength = entryBytes[pos] & 0x7F;
5065 if (entryBytes[pos++] != configLength)
5066 {
5067 int numLengthBytes = configLength;
5068 configLength = 0;
5069 for (int i=0; i < numLengthBytes; i++, pos++)
5070 {
5071 configLength =
5072 (configLength << 8) | (entryBytes[pos] & 0xFF);
5073 }
5074 }
5075
5076
5077 // Next is the encoded configuration itself.
5078 EntryEncodeConfig config =
5079 EntryEncodeConfig.decode(entryBytes, pos, configLength,
5080 compressedSchema);
5081 pos += configLength;
5082
5083
5084 // If we should have included the DN in the entry, then it's
5085 // next.
5086 DN dn;
5087 if (config.excludeDN())
5088 {
5089 dn = DN.NULL_DN;
5090 }
5091 else
5092 {
5093 // Next is the length of the DN. It may be a single byte or
5094 // multiple bytes.
5095 int dnLength = entryBytes[pos] & 0x7F;
5096 if (entryBytes[pos++] != dnLength)
5097 {
5098 int numLengthBytes = dnLength;
5099 dnLength = 0;
5100 for (int i=0; i < numLengthBytes; i++, pos++)
5101 {
5102 dnLength = (dnLength << 8) | (entryBytes[pos] & 0xFF);
5103 }
5104 }
5105
5106
5107 // Next is the DN itself.
5108 byte[] dnBytes = new byte[dnLength];
5109 System.arraycopy(entryBytes, pos, dnBytes, 0, dnLength);
5110 pos += dnLength;
5111 dn = DN.decode(new ASN1OctetString(dnBytes));
5112 }
5113
5114
5115 // Next is the length of the object classes. It may be a single
5116 // byte or multiple bytes.
5117 int ocLength = entryBytes[pos] & 0x7F;
5118 if (entryBytes[pos++] != ocLength)
5119 {
5120 int numLengthBytes = ocLength;
5121 ocLength = 0;
5122 for (int i=0; i < numLengthBytes; i++, pos++)
5123 {
5124 ocLength = (ocLength << 8) | (entryBytes[pos] & 0xFF);
5125 }
5126 }
5127
5128
5129 // Next is the set of encoded object classes. The encoding will
5130 // depend on the configuration.
5131 Map<ObjectClass,String> objectClasses;
5132 if (config.compressObjectClassSets())
5133 {
5134 byte[] ocBytes = new byte[ocLength];
5135 System.arraycopy(entryBytes, pos, ocBytes, 0, ocLength);
5136 objectClasses = config.getCompressedSchema().
5137 decodeObjectClasses(ocBytes);
5138 pos += ocLength;
5139 }
5140 else
5141 {
5142 // The set of object classes will be encoded as a single
5143 // string with the oibject class names separated by zeros.
5144 objectClasses = new LinkedHashMap<ObjectClass,String>();
5145 int startPos = pos;
5146 for (int i=0; i < ocLength; i++,pos++)
5147 {
5148 if (entryBytes[pos] == 0x00)
5149 {
5150 String name = new String(entryBytes, startPos,
5151 pos-startPos, "UTF-8");
5152 String lowerName = toLowerCase(name);
5153 ObjectClass oc =
5154 DirectoryServer.getObjectClass(lowerName, true);
5155 objectClasses.put(oc, name);
5156 startPos = pos+1;
5157 }
5158 }
5159 String name = new String(entryBytes, startPos, pos-startPos,
5160 "UTF-8");
5161 String lowerName = toLowerCase(name);
5162 ObjectClass oc =
5163 DirectoryServer.getObjectClass(lowerName, true);
5164 objectClasses.put(oc, name);
5165 }
5166
5167
5168 // Next is the total number of user attributes. It may be a
5169 // single byte or multiple bytes.
5170 int numUserAttrs = entryBytes[pos] & 0x7F;
5171 if (entryBytes[pos++] != numUserAttrs)
5172 {
5173 int numLengthBytes = numUserAttrs;
5174 numUserAttrs = 0;
5175 for (int i=0; i < numLengthBytes; i++, pos++)
5176 {
5177 numUserAttrs = (numUserAttrs << 8) |
5178 (entryBytes[pos] & 0xFF);
5179 }
5180 }
5181
5182
5183 // Now, we should iterate through the user attributes and decode
5184 // each one.
5185 LinkedHashMap<AttributeType,List<Attribute>> userAttributes =
5186 new LinkedHashMap<AttributeType,List<Attribute>>();
5187 if (config.compressAttributeDescriptions())
5188 {
5189 for (int i=0; i < numUserAttrs; i++)
5190 {
5191 // Get the length of the attribute.
5192 int attrLength = entryBytes[pos] & 0x7F;
5193 if (entryBytes[pos++] != attrLength)
5194 {
5195 int attrLengthBytes = attrLength;
5196 attrLength = 0;
5197 for (int j=0; j < attrLengthBytes; j++, pos++)
5198 {
5199 attrLength = (attrLength << 8) |
5200 (entryBytes[pos] & 0xFF);
5201 }
5202 }
5203
5204 // Decode the attribute.
5205 Attribute a = config.getCompressedSchema().decodeAttribute(
5206 entryBytes, pos, attrLength);
5207 List<Attribute> attrList =
5208 userAttributes.get(a.getAttributeType());
5209 if (attrList == null)
5210 {
5211 attrList = new ArrayList<Attribute>(1);
5212 userAttributes.put(a.getAttributeType(), attrList);
5213 }
5214
5215 attrList.add(a);
5216 pos += attrLength;
5217 }
5218 }
5219 else
5220 {
5221 for (int i=0; i < numUserAttrs; i++)
5222 {
5223 // First, we have the zero-terminated attribute name.
5224 int startPos = pos;
5225 while (entryBytes[pos] != 0x00)
5226 {
5227 pos++;
5228 }
5229
5230 String lowerName;
5231 String name = new String(entryBytes, startPos, pos-startPos,
5232 "UTF-8");
5233 LinkedHashSet<String> options;
5234 int semicolonPos = name.indexOf(';');
5235 if (semicolonPos > 0)
5236 {
5237 String baseName = name.substring(0, semicolonPos);
5238 lowerName = toLowerCase(baseName);
5239 options = new LinkedHashSet<String>();
5240
5241 int nextPos = name.indexOf(';', semicolonPos+1);
5242 while (nextPos > 0)
5243 {
5244 String option = name.substring(semicolonPos+1, nextPos);
5245 if (option.length() > 0)
5246 {
5247 options.add(option);
5248 }
5249
5250 semicolonPos = nextPos;
5251 nextPos = name.indexOf(';', semicolonPos+1);
5252 }
5253
5254 String option = name.substring(semicolonPos+1);
5255 if (option.length() > 0)
5256 {
5257 options.add(option);
5258 }
5259
5260 name = baseName;
5261 }
5262 else
5263 {
5264 lowerName = toLowerCase(name);
5265 options = new LinkedHashSet<String>(0);
5266 }
5267 AttributeType attributeType =
5268 DirectoryServer.getAttributeType(lowerName, true);
5269
5270
5271
5272 // Next, we have the number of values.
5273 int numValues = entryBytes[++pos] & 0x7F;
5274 if (entryBytes[pos++] != numValues)
5275 {
5276 int numLengthBytes = numValues;
5277 numValues = 0;
5278 for (int j=0; j < numLengthBytes; j++, pos++)
5279 {
5280 numValues = (numValues << 8) | (entryBytes[pos] & 0xFF);
5281 }
5282 }
5283
5284 // Next, we have the sequence of length-value pairs.
5285 LinkedHashSet<AttributeValue> values =
5286 new LinkedHashSet<AttributeValue>(numValues);
5287 for (int j=0; j < numValues; j++)
5288 {
5289 int valueLength = entryBytes[pos] & 0x7F;
5290 if (entryBytes[pos++] != valueLength)
5291 {
5292 int numLengthBytes = valueLength;
5293 valueLength = 0;
5294 for (int k=0; k < numLengthBytes; k++, pos++)
5295 {
5296 valueLength = (valueLength << 8) |
5297 (entryBytes[pos] & 0xFF);
5298 }
5299 }
5300
5301 byte[] valueBytes = new byte[valueLength];
5302 System.arraycopy(entryBytes, pos, valueBytes, 0,
5303 valueLength);
5304 values.add(new AttributeValue(attributeType,
5305 new ASN1OctetString(valueBytes)));
5306 pos += valueLength;
5307 }
5308
5309
5310 // Create the attribute and add it to the set of user
5311 // attributes.
5312 Attribute a = new Attribute(attributeType, name, options,
5313 values);
5314 List<Attribute> attrList =
5315 userAttributes.get(attributeType);
5316 if (attrList == null)
5317 {
5318 attrList = new ArrayList<Attribute>(1);
5319 attrList.add(a);
5320 userAttributes.put(attributeType, attrList);
5321 }
5322 else
5323 {
5324 attrList.add(a);
5325 }
5326 }
5327 }
5328
5329
5330 // Next is the total number of operational attributes. It may
5331 // be a single byte or multiple bytes.
5332 int numOperationalAttrs = entryBytes[pos] & 0x7F;
5333 if (entryBytes[pos++] != numOperationalAttrs)
5334 {
5335 int numLengthBytes = numOperationalAttrs;
5336 numOperationalAttrs = 0;
5337 for (int i=0; i < numLengthBytes; i++, pos++)
5338 {
5339 numOperationalAttrs =
5340 (numOperationalAttrs << 8) | (entryBytes[pos] & 0xFF);
5341 }
5342 }
5343
5344
5345 // Now, we should iterate through the operational attributes and
5346 // decode each one.
5347 LinkedHashMap<AttributeType,List<Attribute>>
5348 operationalAttributes =
5349 new LinkedHashMap<AttributeType,List<Attribute>>();
5350 if (config.compressAttributeDescriptions())
5351 {
5352 for (int i=0; i < numOperationalAttrs; i++)
5353 {
5354 // Get the length of the attribute.
5355 int attrLength = entryBytes[pos] & 0x7F;
5356 if (entryBytes[pos++] != attrLength)
5357 {
5358 int attrLengthBytes = attrLength;
5359 attrLength = 0;
5360 for (int j=0; j < attrLengthBytes; j++, pos++)
5361 {
5362 attrLength = (attrLength << 8) |
5363 (entryBytes[pos] & 0xFF);
5364 }
5365 }
5366
5367 // Decode the attribute.
5368 Attribute a = config.getCompressedSchema().decodeAttribute(
5369 entryBytes, pos, attrLength);
5370 List<Attribute> attrList =
5371 operationalAttributes.get(a.getAttributeType());
5372 if (attrList == null)
5373 {
5374 attrList = new ArrayList<Attribute>(1);
5375 operationalAttributes.put(a.getAttributeType(), attrList);
5376 }
5377
5378 attrList.add(a);
5379 pos += attrLength;
5380 }
5381 }
5382 else
5383 {
5384 for (int i=0; i < numOperationalAttrs; i++)
5385 {
5386 // First, we have the zero-terminated attribute name.
5387 int startPos = pos;
5388 while (entryBytes[pos] != 0x00)
5389 {
5390 pos++;
5391 }
5392 String lowerName;
5393 String name = new String(entryBytes, startPos, pos-startPos,
5394 "UTF-8");
5395 LinkedHashSet<String> options;
5396 int semicolonPos = name.indexOf(';');
5397 if (semicolonPos > 0)
5398 {
5399 String baseName = name.substring(0, semicolonPos);
5400 lowerName = toLowerCase(baseName);
5401 options = new LinkedHashSet<String>();
5402
5403 int nextPos = name.indexOf(';', semicolonPos+1);
5404 while (nextPos > 0)
5405 {
5406 String option = name.substring(semicolonPos+1, nextPos);
5407 if (option.length() > 0)
5408 {
5409 options.add(option);
5410 }
5411
5412 semicolonPos = nextPos;
5413 nextPos = name.indexOf(';', semicolonPos+1);
5414 }
5415
5416 String option = name.substring(semicolonPos+1);
5417 if (option.length() > 0)
5418 {
5419 options.add(option);
5420 }
5421
5422 name = baseName;
5423 }
5424 else
5425 {
5426 lowerName = toLowerCase(name);
5427 options = new LinkedHashSet<String>(0);
5428 }
5429 AttributeType attributeType =
5430 DirectoryServer.getAttributeType(lowerName, true);
5431
5432
5433 // Next, we have the number of values.
5434 int numValues = entryBytes[++pos] & 0x7F;
5435 if (entryBytes[pos++] != numValues)
5436 {
5437 int numLengthBytes = numValues;
5438 numValues = 0;
5439 for (int j=0; j < numLengthBytes; j++, pos++)
5440 {
5441 numValues = (numValues << 8) | (entryBytes[pos] & 0xFF);
5442 }
5443 }
5444
5445 // Next, we have the sequence of length-value pairs.
5446 LinkedHashSet<AttributeValue> values =
5447 new LinkedHashSet<AttributeValue>(numValues);
5448 for (int j=0; j < numValues; j++)
5449 {
5450 int valueLength = entryBytes[pos] & 0x7F;
5451 if (entryBytes[pos++] != valueLength)
5452 {
5453 int numLengthBytes = valueLength;
5454 valueLength = 0;
5455 for (int k=0; k < numLengthBytes; k++, pos++)
5456 {
5457 valueLength = (valueLength << 8) |
5458 (entryBytes[pos] & 0xFF);
5459 }
5460 }
5461
5462 byte[] valueBytes = new byte[valueLength];
5463 System.arraycopy(entryBytes, pos, valueBytes, 0,
5464 valueLength);
5465 values.add(new AttributeValue(attributeType,
5466 new ASN1OctetString(valueBytes)));
5467 pos += valueLength;
5468 }
5469
5470
5471 // Create the attribute and add it to the set of operational
5472 // attributes.
5473 Attribute a = new Attribute(attributeType, name, options,
5474 values);
5475 List<Attribute> attrList =
5476 operationalAttributes.get(attributeType);
5477 if (attrList == null)
5478 {
5479 attrList = new ArrayList<Attribute>(1);
5480 attrList.add(a);
5481 operationalAttributes.put(attributeType, attrList);
5482 }
5483 else
5484 {
5485 attrList.add(a);
5486 }
5487 }
5488 }
5489
5490
5491 // We've got everything that we need, so create and return the
5492 // entry.
5493 return new Entry(dn, objectClasses, userAttributes,
5494 operationalAttributes);
5495 }
5496 catch (DirectoryException de)
5497 {
5498 throw de;
5499 }
5500 catch (Exception e)
5501 {
5502 if (debugEnabled())
5503 {
5504 TRACER.debugCaught(DebugLogLevel.ERROR, e);
5505 }
5506
5507 Message message =
5508 ERR_ENTRY_DECODE_EXCEPTION.get(getExceptionMessage(e));
5509 throw new DirectoryException(
5510 DirectoryServer.getServerErrorResultCode(),
5511 message, e);
5512 }
5513 }
5514
5515
5516
5517 /**
5518 * Retrieves a list of the lines for this entry in LDIF form. Long
5519 * lines will not be wrapped automatically.
5520 *
5521 * @return A list of the lines for this entry in LDIF form.
5522 */
5523 public List<StringBuilder> toLDIF()
5524 {
5525 LinkedList<StringBuilder> ldifLines =
5526 new LinkedList<StringBuilder>();
5527
5528
5529 // First, append the DN.
5530 StringBuilder dnLine = new StringBuilder();
5531 dnLine.append("dn");
5532 appendLDIFSeparatorAndValue(dnLine, getBytes(dn.toString()));
5533 ldifLines.add(dnLine);
5534
5535
5536 // Next, add the set of objectclasses.
5537 for (String s : objectClasses.values())
5538 {
5539 StringBuilder ocLine = new StringBuilder();
5540 ocLine.append("objectClass: ");
5541 ocLine.append(s);
5542 ldifLines.add(ocLine);
5543 }
5544
5545
5546 // Next, add the set of user attributes.
5547 for (List<Attribute> attrList : userAttributes.values())
5548 {
5549 for (Attribute a : attrList)
5550 {
5551 StringBuilder attrName = new StringBuilder(a.getName());
5552 for (String o : a.getOptions())
5553 {
5554 attrName.append(";");
5555 attrName.append(o);
5556 }
5557
5558 for (AttributeValue v : a.getValues())
5559 {
5560 StringBuilder attrLine = new StringBuilder();
5561 attrLine.append(attrName);
5562 appendLDIFSeparatorAndValue(attrLine, v.getValueBytes());
5563 ldifLines.add(attrLine);
5564 }
5565 }
5566 }
5567
5568
5569 // Finally, add the set of operational attributes.
5570 for (List<Attribute> attrList : operationalAttributes.values())
5571 {
5572 for (Attribute a : attrList)
5573 {
5574 StringBuilder attrName = new StringBuilder(a.getName());
5575 for (String o : a.getOptions())
5576 {
5577 attrName.append(";");
5578 attrName.append(o);
5579 }
5580
5581 for (AttributeValue v : a.getValues())
5582 {
5583 StringBuilder attrLine = new StringBuilder();
5584 attrLine.append(attrName);
5585 appendLDIFSeparatorAndValue(attrLine, v.getValueBytes());
5586 ldifLines.add(attrLine);
5587 }
5588 }
5589 }
5590
5591
5592 return ldifLines;
5593 }
5594
5595
5596
5597 /**
5598 * Writes this entry in LDIF form according to the provided
5599 * configuration.
5600 *
5601 * @param exportConfig The configuration that specifies how the
5602 * entry should be written.
5603 *
5604 * @return <CODE>true</CODE> if the entry is actually written, or
5605 * <CODE>false</CODE> if it is not for some reason.
5606 *
5607 * @throws IOException If a problem occurs while writing the
5608 * information.
5609 *
5610 * @throws LDIFException If a problem occurs while trying to
5611 * determine whether to write the entry.
5612 */
5613 public boolean toLDIF(LDIFExportConfig exportConfig)
5614 throws IOException, LDIFException
5615 {
5616 // See if this entry should be included in the export at all.
5617 try
5618 {
5619 if (! exportConfig.includeEntry(this))
5620 {
5621 if (debugEnabled())
5622 {
5623 TRACER.debugInfo(
5624 "Skipping entry %s because of the export " +
5625 "configuration.", String.valueOf(dn));
5626 }
5627 return false;
5628 }
5629 }
5630 catch (Exception e)
5631 {
5632 if (debugEnabled())
5633 {
5634 TRACER.debugCaught(DebugLogLevel.ERROR, e);
5635 }
5636
5637 Message message =
5638 ERR_LDIF_COULD_NOT_EVALUATE_FILTERS_FOR_EXPORT.
5639 get(String.valueOf(dn), String.valueOf(e));
5640 throw new LDIFException(message, e);
5641 }
5642
5643
5644 // Invoke LDIF export plugins on the entry if appropriate.
5645 if (exportConfig.invokeExportPlugins())
5646 {
5647 PluginConfigManager pluginConfigManager =
5648 DirectoryServer.getPluginConfigManager();
5649 PluginResult.ImportLDIF pluginResult =
5650 pluginConfigManager.invokeLDIFExportPlugins(exportConfig,
5651 this);
5652 if (! pluginResult.continueProcessing())
5653 {
5654 return false;
5655 }
5656 }
5657
5658
5659 // Get the information necessary to write the LDIF.
5660 BufferedWriter writer = exportConfig.getWriter();
5661 int wrapColumn = exportConfig.getWrapColumn();
5662 boolean wrapLines = (wrapColumn > 1);
5663
5664
5665 // First, write the DN. It will always be included.
5666 StringBuilder dnLine = new StringBuilder();
5667 dnLine.append("dn");
5668 appendLDIFSeparatorAndValue(dnLine, getBytes(dn.toString()));
5669 writeLDIFLine(dnLine, writer, wrapLines, wrapColumn);
5670
5671
5672 // Next, the set of objectclasses.
5673 final boolean typesOnly = exportConfig.typesOnly();
5674 if (exportConfig.includeObjectClasses())
5675 {
5676 if (typesOnly)
5677 {
5678 StringBuilder ocLine = new StringBuilder("objectClass:");
5679 writeLDIFLine(ocLine, writer, wrapLines, wrapColumn);
5680 }
5681 else
5682 {
5683 for (String s : objectClasses.values())
5684 {
5685 StringBuilder ocLine = new StringBuilder();
5686 ocLine.append("objectClass: ");
5687 ocLine.append(s);
5688 writeLDIFLine(ocLine, writer, wrapLines, wrapColumn);
5689 }
5690 }
5691 }
5692 else
5693 {
5694 if (debugEnabled())
5695 {
5696 TRACER.debugVerbose(
5697 "Skipping objectclasses for entry %s because of " +
5698 "the export configuration.", String.valueOf(dn));
5699 }
5700 }
5701
5702
5703 // Now the set of user attributes.
5704 for (AttributeType attrType : userAttributes.keySet())
5705 {
5706 if (exportConfig.includeAttribute(attrType))
5707 {
5708 List<Attribute> attrList = userAttributes.get(attrType);
5709 for (Attribute a : attrList)
5710 {
5711 if (a.isVirtual() &&
5712 (! exportConfig.includeVirtualAttributes()))
5713 {
5714 continue;
5715 }
5716
5717 if (typesOnly)
5718 {
5719 StringBuilder attrName = new StringBuilder(a.getName());
5720 for (String o : a.getOptions())
5721 {
5722 attrName.append(";");
5723 attrName.append(o);
5724 }
5725 attrName.append(":");
5726
5727 writeLDIFLine(attrName, writer, wrapLines, wrapColumn);
5728 }
5729 else
5730 {
5731 StringBuilder attrName = new StringBuilder(a.getName());
5732 for (String o : a.getOptions())
5733 {
5734 attrName.append(";");
5735 attrName.append(o);
5736 }
5737
5738 for (AttributeValue v : a.getValues())
5739 {
5740 StringBuilder attrLine = new StringBuilder();
5741 attrLine.append(attrName);
5742 appendLDIFSeparatorAndValue(attrLine,
5743 v.getValueBytes());
5744 writeLDIFLine(attrLine, writer, wrapLines, wrapColumn);
5745 }
5746 }
5747 }
5748 }
5749 else
5750 {
5751 if (debugEnabled())
5752 {
5753 TRACER.debugVerbose(
5754 "Skipping user attribute %s for entry %s because of " +
5755 "the export configuration.",
5756 attrType.getNameOrOID(), String.valueOf(dn));
5757 }
5758 }
5759 }
5760
5761
5762 // Next, the set of operational attributes.
5763 if (exportConfig.includeOperationalAttributes())
5764 {
5765 for (AttributeType attrType : operationalAttributes.keySet())
5766 {
5767 if (exportConfig.includeAttribute(attrType))
5768 {
5769 List<Attribute> attrList =
5770 operationalAttributes.get(attrType);
5771 for (Attribute a : attrList)
5772 {
5773 if (a.isVirtual() &&
5774 (! exportConfig.includeVirtualAttributes()))
5775 {
5776 continue;
5777 }
5778
5779 if (typesOnly)
5780 {
5781 StringBuilder attrName = new StringBuilder(a.getName());
5782 for (String o : a.getOptions())
5783 {
5784 attrName.append(";");
5785 attrName.append(o);
5786 }
5787 attrName.append(":");
5788
5789 writeLDIFLine(attrName, writer, wrapLines, wrapColumn);
5790 }
5791 else
5792 {
5793 StringBuilder attrName = new StringBuilder(a.getName());
5794 for (String o : a.getOptions())
5795 {
5796 attrName.append(";");
5797 attrName.append(o);
5798 }
5799
5800 for (AttributeValue v : a.getValues())
5801 {
5802 StringBuilder attrLine = new StringBuilder();
5803 attrLine.append(attrName);
5804 appendLDIFSeparatorAndValue(attrLine,
5805 v.getValueBytes());
5806 writeLDIFLine(attrLine, writer, wrapLines,
5807 wrapColumn);
5808 }
5809 }
5810 }
5811 }
5812 else
5813 {
5814 if (debugEnabled())
5815 {
5816 TRACER.debugVerbose(
5817 "Skipping operational attribute %s for entry %s " +
5818 "because of the export configuration.",
5819 attrType.getNameOrOID(), String.valueOf(dn));
5820 }
5821 }
5822 }
5823 }
5824 else
5825 {
5826 if (debugEnabled())
5827 {
5828 TRACER.debugVerbose(
5829 "Skipping all operational attributes for entry %s " +
5830 "because of the export configuration.",
5831 String.valueOf(dn));
5832 }
5833 }
5834
5835
5836 // If we are not supposed to include virtual attributes, then
5837 // write any attributes that may normally be suppressed by a
5838 // virtual attribute.
5839 if (! exportConfig.includeVirtualAttributes())
5840 {
5841 for (AttributeType t : suppressedAttributes.keySet())
5842 {
5843 if (exportConfig.includeAttribute(t))
5844 {
5845 for (Attribute a : suppressedAttributes.get(t))
5846 {
5847 if (typesOnly)
5848 {
5849 StringBuilder attrName = new StringBuilder(a.getName());
5850 for (String o : a.getOptions())
5851 {
5852 attrName.append(";");
5853 attrName.append(o);
5854 }
5855 attrName.append(":");
5856
5857 writeLDIFLine(attrName, writer, wrapLines, wrapColumn);
5858 }
5859 else
5860 {
5861 StringBuilder attrName = new StringBuilder(a.getName());
5862 for (String o : a.getOptions())
5863 {
5864 attrName.append(";");
5865 attrName.append(o);
5866 }
5867
5868 for (AttributeValue v : a.getValues())
5869 {
5870 StringBuilder attrLine = new StringBuilder();
5871 attrLine.append(attrName);
5872 appendLDIFSeparatorAndValue(attrLine,
5873 v.getValueBytes());
5874 writeLDIFLine(attrLine, writer, wrapLines,
5875 wrapColumn);
5876 }
5877 }
5878 }
5879 }
5880 }
5881 }
5882
5883
5884 // Make sure there is a blank line after the entry.
5885 writer.newLine();
5886
5887
5888 return true;
5889 }
5890
5891
5892
5893 /**
5894 * Retrieves the name of the protocol associated with this protocol
5895 * element.
5896 *
5897 * @return The name of the protocol associated with this protocol
5898 * element.
5899 */
5900 public String getProtocolElementName()
5901 {
5902 return "Entry";
5903 }
5904
5905
5906
5907 /**
5908 * Retrieves a hash code for this entry.
5909 *
5910 * @return The hash code for this entry.
5911 */
5912 @Override()
5913 public int hashCode()
5914 {
5915 int hashCode = dn.hashCode();
5916
5917 for (ObjectClass oc : objectClasses.keySet())
5918 {
5919 hashCode += oc.hashCode();
5920 }
5921
5922 for (List<Attribute> attrList : userAttributes.values())
5923 {
5924 for (Attribute a : attrList)
5925 {
5926 hashCode += a.hashCode();
5927 }
5928 }
5929
5930 for (List<Attribute> attrList : operationalAttributes.values())
5931 {
5932 for (Attribute a : attrList)
5933 {
5934 hashCode += a.hashCode();
5935 }
5936 }
5937
5938 return hashCode;
5939 }
5940
5941
5942
5943 /**
5944 * Indicates whether the provided object is equal to this entry. In
5945 * order for the object to be considered equal, it must be an entry
5946 * with the same DN, set of object classes, and set of user and
5947 * operational attributes.
5948 *
5949 * @param o The object for which to make the determination.
5950 *
5951 * @return {@code true} if the provided object may be considered
5952 * equal to this entry, or {@code false} if not.
5953 */
5954 @Override()
5955 public boolean equals(Object o)
5956 {
5957 if (this == o)
5958 {
5959 return true;
5960 }
5961
5962 if (o == null)
5963 {
5964 return false;
5965 }
5966
5967 if (! (o instanceof Entry))
5968 {
5969 return false;
5970 }
5971
5972 Entry e = (Entry) o;
5973 if (! dn.equals(e.dn))
5974 {
5975 return false;
5976 }
5977
5978 if (! objectClasses.keySet().equals(e.objectClasses.keySet()))
5979 {
5980 return false;
5981 }
5982
5983 for (AttributeType at : userAttributes.keySet())
5984 {
5985 List<Attribute> list1 = userAttributes.get(at);
5986 List<Attribute> list2 = e.userAttributes.get(at);
5987 if ((list2 == null) || (list1.size() != list2.size()))
5988 {
5989 return false;
5990 }
5991 for (Attribute a : list1)
5992 {
5993 if (! list2.contains(a))
5994 {
5995 return false;
5996 }
5997 }
5998 }
5999
6000 for (AttributeType at : operationalAttributes.keySet())
6001 {
6002 List<Attribute> list1 = operationalAttributes.get(at);
6003 List<Attribute> list2 = e.operationalAttributes.get(at);
6004 if ((list2 == null) || (list1.size() != list2.size()))
6005 {
6006 return false;
6007 }
6008 for (Attribute a : list1)
6009 {
6010 if (! list2.contains(a))
6011 {
6012 return false;
6013 }
6014 }
6015 }
6016
6017 return true;
6018 }
6019
6020
6021
6022 /**
6023 * Retrieves a string representation of this protocol element.
6024 *
6025 * @return A string representation of this protocol element.
6026 */
6027 public String toString()
6028 {
6029 StringBuilder buffer = new StringBuilder();
6030 toString(buffer);
6031 return buffer.toString();
6032 }
6033
6034
6035
6036 /**
6037 * Appends a string representation of this protocol element to the
6038 * provided buffer.
6039 *
6040 * @param buffer The buffer into which the string representation
6041 * should be written.
6042 */
6043 public void toString(StringBuilder buffer)
6044 {
6045 buffer.append("Entry(dn=\"");
6046 dn.toString(buffer);
6047
6048 buffer.append("\", objectClasses={");
6049 if (! objectClasses.isEmpty())
6050 {
6051 Iterator<String> ocNames = objectClasses.values().iterator();
6052 buffer.append(ocNames.next());
6053
6054 while (ocNames.hasNext())
6055 {
6056 buffer.append(",");
6057 buffer.append(ocNames.next());
6058 }
6059 }
6060
6061 buffer.append("}, userAttrs={");
6062 if (! userAttributes.isEmpty())
6063 {
6064 Iterator<AttributeType> attrs =
6065 userAttributes.keySet().iterator();
6066 buffer.append(attrs.next().getNameOrOID());
6067
6068 while (attrs.hasNext())
6069 {
6070 buffer.append(",");
6071 buffer.append(attrs.next().getNameOrOID());
6072 }
6073 }
6074
6075 buffer.append("}, operationalAttrs={");
6076 if (! operationalAttributes.isEmpty())
6077 {
6078 Iterator<AttributeType> attrs =
6079 operationalAttributes.keySet().iterator();
6080 buffer.append(attrs.next().getNameOrOID());
6081
6082 while (attrs.hasNext())
6083 {
6084 buffer.append(",");
6085 buffer.append(attrs.next().getNameOrOID());
6086 }
6087 }
6088
6089 buffer.append("})");
6090 }
6091
6092
6093
6094 /**
6095 * Appends a string representation of this protocol element to the
6096 * provided buffer.
6097 *
6098 * @param buffer The buffer into which the string representation
6099 * should be written.
6100 * @param indent The number of spaces that should be used to
6101 * indent the resulting string representation.
6102 */
6103 public void toString(StringBuilder buffer, int indent)
6104 {
6105 StringBuilder indentBuf = new StringBuilder(indent);
6106 for (int i=0 ; i < indent; i++)
6107 {
6108 indentBuf.append(' ');
6109 }
6110
6111 for (StringBuilder b : toLDIF())
6112 {
6113 buffer.append(indentBuf);
6114 buffer.append(b);
6115 buffer.append(EOL);
6116 }
6117 }
6118
6119
6120
6121 /**
6122 * Retrieves a string representation of this entry in LDIF form.
6123 *
6124 * @return A string representation of this entry in LDIF form.
6125 */
6126 public String toLDIFString()
6127 {
6128 StringBuilder buffer = new StringBuilder();
6129
6130 for (StringBuilder ldifLine : toLDIF())
6131 {
6132 buffer.append(ldifLine);
6133 buffer.append(EOL);
6134 }
6135
6136 return buffer.toString();
6137 }
6138
6139
6140
6141 /**
6142 * Retrieves a one-line representation of this entry.
6143 *
6144 * @return A one-line representation of this entry.
6145 */
6146 public String toSingleLineString()
6147 {
6148 StringBuilder buffer = new StringBuilder();
6149 toSingleLineString(buffer);
6150 return buffer.toString();
6151 }
6152
6153
6154
6155 /**
6156 * Appends a single-line representation of this entry to the
6157 * provided buffer.
6158 *
6159 * @param buffer The buffer to which the information should be
6160 * written.
6161 */
6162 public void toSingleLineString(StringBuilder buffer)
6163 {
6164 buffer.append("Entry(dn=\"");
6165 dn.toString(buffer);
6166 buffer.append("\",objectClasses={");
6167
6168 Iterator<String> iterator = objectClasses.values().iterator();
6169 if (iterator.hasNext())
6170 {
6171 buffer.append(iterator.next());
6172
6173 while (iterator.hasNext())
6174 {
6175 buffer.append(",");
6176 buffer.append(iterator.next());
6177 }
6178 }
6179
6180 buffer.append("},userAttrs={");
6181
6182 boolean firstAttr = true;
6183 for (List<Attribute> attrList : userAttributes.values())
6184 {
6185 for (Attribute a : attrList)
6186 {
6187 if (firstAttr)
6188 {
6189 firstAttr = false;
6190 }
6191 else
6192 {
6193 buffer.append(",");
6194 }
6195
6196 buffer.append(a.getName());
6197
6198 if (a.hasOptions())
6199 {
6200 for (String optionString : a.getOptions())
6201 {
6202 buffer.append(";");
6203 buffer.append(optionString);
6204 }
6205 }
6206
6207 buffer.append("={");
6208 Iterator<AttributeValue> valueIterator =
6209 a.getValues().iterator();
6210 if (valueIterator.hasNext())
6211 {
6212 buffer.append(valueIterator.next().getStringValue());
6213
6214 while (valueIterator.hasNext())
6215 {
6216 buffer.append(",");
6217 buffer.append(valueIterator.next().getStringValue());
6218 }
6219 }
6220
6221 buffer.append("}");
6222 }
6223 }
6224
6225 buffer.append("},operationalAttrs={");
6226 for (List<Attribute> attrList : operationalAttributes.values())
6227 {
6228 for (Attribute a : attrList)
6229 {
6230 if (firstAttr)
6231 {
6232 firstAttr = false;
6233 }
6234 else
6235 {
6236 buffer.append(",");
6237 }
6238
6239 buffer.append(a.getName());
6240
6241 if (a.hasOptions())
6242 {
6243 for (String optionString : a.getOptions())
6244 {
6245 buffer.append(";");
6246 buffer.append(optionString);
6247 }
6248 }
6249
6250 buffer.append("={");
6251 Iterator<AttributeValue> valueIterator =
6252 a.getValues().iterator();
6253 if (valueIterator.hasNext())
6254 {
6255 buffer.append(valueIterator.next().getStringValue());
6256
6257 while (valueIterator.hasNext())
6258 {
6259 buffer.append(",");
6260 buffer.append(valueIterator.next().getStringValue());
6261 }
6262 }
6263
6264 buffer.append("}");
6265 }
6266 }
6267
6268 buffer.append("})");
6269 }
6270 }
6271