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
029
030
031 import java.util.ArrayList;
032 import java.util.Collection;
033 import java.util.LinkedHashSet;
034 import java.util.List;
035 import java.util.Set;
036
037 import org.opends.server.api.ApproximateMatchingRule;
038 import org.opends.server.api.OrderingMatchingRule;
039 import org.opends.server.api.SubstringMatchingRule;
040 import org.opends.server.core.DirectoryServer;
041 import org.opends.server.util.Base64;
042
043 import static org.opends.server.loggers.debug.DebugLogger.*;
044 import org.opends.server.loggers.debug.DebugTracer;
045 import static org.opends.server.util.ServerConstants.*;
046 import static org.opends.server.util.StaticUtils.*;
047
048
049
050 /**
051 * This class defines a data structure for storing and interacting
052 * with an attribute that may be used in the Directory Server.
053 */
054 @org.opends.server.types.PublicAPI(
055 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
056 mayInstantiate=true,
057 mayExtend=false,
058 mayInvoke=true)
059 public class Attribute
060 {
061 /**
062 * The tracer object for the debug logger.
063 */
064 private static final DebugTracer TRACER = getTracer();
065
066 // The attribute type for this attribute.
067 private final AttributeType attributeType;
068
069 // The set of values for this attribute.
070 private LinkedHashSet<AttributeValue> values;
071
072 // The set of options for this attribute.
073 private final LinkedHashSet<String> options;
074
075 // The set of options for this attribute, formatted in all lowercase
076 // characters.
077 private final LinkedHashSet<String> lowerOptions;
078
079 // The name of this attribute as provided by the end user.
080 private final String name;
081
082
083
084 /**
085 * Creates a new attribute with the specified type. It will not
086 * have any values.
087 *
088 * @param attributeType The attribute type for this attribute.
089 */
090 public Attribute(AttributeType attributeType)
091 {
092 this.attributeType = attributeType;
093 this.name = attributeType.getPrimaryName();
094 this.options = new LinkedHashSet<String>(0);
095 this.values = new LinkedHashSet<AttributeValue>();
096
097 lowerOptions = options;
098 }
099
100
101
102 /**
103 * Creates a new attribute with the specified type and user-provided
104 * name. It will not have any values.
105 *
106 * @param attributeType The attribute type for this attribute.
107 * @param name The user-provided name for this attribute.
108 */
109 public Attribute(AttributeType attributeType, String name)
110 {
111 this.attributeType = attributeType;
112 this.name = name;
113 this.options = new LinkedHashSet<String>(0);
114 this.values = new LinkedHashSet<AttributeValue>();
115
116 lowerOptions = options;
117 }
118
119
120
121 /**
122 * Creates a new attribute with the specified type, user-provided
123 * name, and set of values.
124 *
125 * @param attributeType The attribute type for this attribute.
126 * @param name The user-provided name for this attribute.
127 * @param values The set of values for this attribute.
128 */
129 public Attribute(AttributeType attributeType, String name,
130 LinkedHashSet<AttributeValue> values)
131 {
132 this.attributeType = attributeType;
133 this.name = name;
134 this.options = new LinkedHashSet<String>(0);
135
136 lowerOptions = options;
137
138 if (values == null)
139 {
140 this.values = new LinkedHashSet<AttributeValue>();
141 }
142 else
143 {
144 this.values = values;
145 }
146 }
147
148
149
150 /**
151 * Creates a new attribute with the specified name and value.
152 *
153 * @param lowerName The name or OID of the attribute type for
154 * this attribute, formatted in all lowercase
155 * characters.
156 * @param valueString The String representation of the attribute
157 * value.
158 */
159 public Attribute(String lowerName, String valueString)
160 {
161 this.attributeType =
162 DirectoryServer.getAttributeType(lowerName, true);
163 this.name = lowerName;
164 this.values = new LinkedHashSet<AttributeValue>();
165 this.values.add(new AttributeValue(this.attributeType,
166 valueString));
167 this.options = new LinkedHashSet<String>(0);
168
169 lowerOptions = options;
170 }
171
172
173
174 /**
175 * Creates a new attribute with the specified type, user-provided
176 * name, and set of values.
177 *
178 * @param attributeType The attribute type for this attribute.
179 * @param name The user-provided name for this attribute.
180 * @param options The set of options for this attribute.
181 * @param values The set of values for this attribute.
182 */
183 public Attribute(AttributeType attributeType, String name,
184 LinkedHashSet<String> options,
185 LinkedHashSet<AttributeValue> values)
186 {
187 this.attributeType = attributeType;
188 this.name = name;
189
190 if ((options == null) || options.isEmpty())
191 {
192 this.options = new LinkedHashSet<String>(0);
193 lowerOptions = options;
194 }
195 else
196 {
197 this.options = options;
198 lowerOptions = new LinkedHashSet<String>(options.size());
199 for (String option : options)
200 {
201 lowerOptions.add(toLowerCase(option));
202 }
203 }
204
205 if (values == null)
206 {
207 this.values = new LinkedHashSet<AttributeValue>(0);
208 }
209 else
210 {
211 this.values = values;
212 }
213 }
214
215
216
217 /**
218 * Retrieves the attribute type for this attribute.
219 *
220 * @return The attribute type for this attribute.
221 */
222 public AttributeType getAttributeType()
223 {
224 return attributeType;
225 }
226
227
228
229 /**
230 * Retrieves the user-provided name for this attribute.
231 *
232 * @return The user-provided name for this attribute.
233 */
234 public String getName()
235 {
236 return name;
237 }
238
239
240
241 /**
242 * Retrieves the user-provided name of the attribute, along with any
243 * options that might have been provided.
244 *
245 * @return The user-provided name of the attribute, along with any
246 * options that might have been provided.
247 */
248 public String getNameWithOptions()
249 {
250 if (options.isEmpty())
251 {
252 return name;
253 }
254 else
255 {
256 StringBuilder buffer = new StringBuilder();
257 buffer.append(name);
258 for (String option : options)
259 {
260 buffer.append(';');
261 buffer.append(option);
262 }
263 return buffer.toString();
264 }
265 }
266
267
268
269 /**
270 * Retrieves the set of attribute options for this attribute.
271 *
272 * @return The set of attribute options for this attribute.
273 */
274 public LinkedHashSet<String> getOptions()
275 {
276 return options;
277 }
278
279
280
281 /**
282 * Indicates whether this attribute has the specified option.
283 *
284 * @param option The option for which to make the determination.
285 *
286 * @return <CODE>true</CODE> if this attribute has the specified
287 * option, or <CODE>false</CODE> if not.
288 */
289 public boolean hasOption(String option)
290 {
291 return lowerOptions.contains(toLowerCase(option));
292 }
293
294
295
296 /**
297 * Indicates whether this attribute has any options at all.
298 *
299 * @return <CODE>true</CODE> if this attribute has at least one
300 * option, or <CODE>false</CODE> if not.
301 */
302 public boolean hasOptions()
303 {
304 return (! options.isEmpty());
305 }
306
307
308
309 /**
310 * Indicates whether this attribute has all of the options in the
311 * provided collection.
312 *
313 * @param options The collection of options for which to make the
314 * determination.
315 *
316 * @return <CODE>true</CODE> if this attribute has all of the
317 * specified options, or <CODE>false</CODE> if it does not
318 * have at least one of them.
319 */
320 public boolean hasOptions(Collection<String> options)
321 {
322 if (options == null)
323 {
324 return true;
325 }
326
327 for (String option : options)
328 {
329 if (! lowerOptions.contains(toLowerCase(option)))
330 {
331 return false;
332 }
333 }
334
335 return true;
336 }
337
338
339
340 /**
341 * Indicates whether this attribute has exactly the set of options
342 * in the provided set.
343 *
344 * @param options The set of options for which to make the
345 * determination.
346 *
347 * @return <CODE>true</CODE> if this attribute has exactly the
348 * specified set of options, or <CODE>false</CODE> if the
349 * set of options is different in any way.
350 */
351 public boolean optionsEqual(Set<String> options)
352 {
353 if (options == null)
354 {
355 return this.options == null || this.options.isEmpty();
356 }
357
358 if (options.isEmpty() && this.options.isEmpty())
359 {
360 return true;
361 }
362
363 if (options.size() != this.options.size())
364 {
365 return false;
366 }
367
368 for (String s : options)
369 {
370 if (! lowerOptions.contains(toLowerCase(s)))
371 {
372 return false;
373 }
374 }
375
376 return true;
377 }
378
379
380
381 /**
382 * Retrieves the set of values for this attribute. The returned set
383 * of values may be altered by the caller.
384 *
385 * @return The set of values for this attribute.
386 */
387 public LinkedHashSet<AttributeValue> getValues()
388 {
389 return values;
390 }
391
392
393
394 /**
395 * Specifies the set of values for this attribute.
396 *
397 * @param values The set of values for this attribute.
398 */
399 public void setValues(LinkedHashSet<AttributeValue> values)
400 {
401 if (values == null)
402 {
403 this.values = new LinkedHashSet<AttributeValue>();
404 }
405 else
406 {
407 this.values = values;
408 }
409 }
410
411
412
413 /**
414 * Indicates whether this attribute contains one or more values.
415 *
416 * @return <CODE>true</CODE> if this attribute contains one or more
417 * values, or <CODE>false</CODE> if it does not.
418 */
419 public boolean hasValue()
420 {
421 return (! getValues().isEmpty());
422 }
423
424
425
426 /**
427 * Indicates whether this attribute contains the specified value.
428 *
429 * @param value The value for which to make the determination.
430 *
431 * @return <CODE>true</CODE> if this attribute has the specified
432 * value, or <CODE>false</CODE> if not.
433 */
434 public boolean hasValue(AttributeValue value)
435 {
436 return getValues().contains(value);
437 }
438
439
440
441 /**
442 * Indicates whether this attribute contains all the values in the
443 * collection.
444 *
445 * @param values The set of values for which to make the
446 * determination.
447 *
448 * @return <CODE>true</CODE> if this attribute contains all the
449 * values in the provided collection, or <CODE>false</CODE>
450 * if it does not contain at least one of them.
451 */
452 public boolean hasAllValues(Collection<AttributeValue> values)
453 {
454 for (AttributeValue value : values)
455 {
456 if (! getValues().contains(value))
457 {
458 return false;
459 }
460 }
461
462 return true;
463 }
464
465
466
467 /**
468 * Indicates whether this attribute contains any of the values in
469 * the collection.
470 *
471 * @param values The set of values for which to make the
472 * determination.
473 *
474 * @return <CODE>true</CODE> if this attribute contains at least
475 * one of the values in the provided collection, or
476 * <CODE>false</CODE> if it does not contain any of the
477 * values.
478 */
479 public boolean hasAnyValue(Collection<AttributeValue> values)
480 {
481 for (AttributeValue value : values)
482 {
483 if (getValues().contains(value))
484 {
485 return true;
486 }
487 }
488
489 return false;
490 }
491
492
493
494 /**
495 * Indicates whether this attribute has any value(s) that match the
496 * provided substring.
497 *
498 * @param subInitial The subInitial component to use in the
499 * determination.
500 * @param subAny The subAny components to use in the
501 * determination.
502 * @param subFinal The subFinal component to use in the
503 * determination.
504 *
505 * @return <CODE>UNDEFINED</CODE> if this attribute does not have a
506 * substring matching rule, <CODE>TRUE</CODE> if at least
507 * one value matches the provided substring, or
508 * <CODE>FALSE</CODE> otherwise.
509 */
510 public ConditionResult matchesSubstring(ByteString subInitial,
511 List<ByteString> subAny,
512 ByteString subFinal)
513 {
514 SubstringMatchingRule matchingRule =
515 attributeType.getSubstringMatchingRule();
516 if (matchingRule == null)
517 {
518 return ConditionResult.UNDEFINED;
519 }
520
521
522 ByteString normalizedSubInitial;
523 if (subInitial == null)
524 {
525 normalizedSubInitial = null;
526 }
527 else
528 {
529 try
530 {
531 normalizedSubInitial =
532 matchingRule.normalizeSubstring(subInitial);
533 }
534 catch (Exception e)
535 {
536 if (debugEnabled())
537 {
538 TRACER.debugCaught(DebugLogLevel.ERROR, e);
539 }
540
541 // The substring couldn't be normalized. We have to return
542 // "undefined".
543 return ConditionResult.UNDEFINED;
544 }
545 }
546
547
548 ArrayList<ByteString> normalizedSubAny;
549 if (subAny == null)
550 {
551 normalizedSubAny = null;
552 }
553 else
554 {
555 normalizedSubAny =
556 new ArrayList<ByteString>(subAny.size());
557 for (ByteString subAnyElement : subAny)
558 {
559 try
560 {
561 normalizedSubAny.add(matchingRule.normalizeSubstring(
562 subAnyElement));
563 }
564 catch (Exception e)
565 {
566 if (debugEnabled())
567 {
568 TRACER.debugCaught(DebugLogLevel.ERROR, e);
569 }
570
571 // The substring couldn't be normalized. We have to return
572 // "undefined".
573 return ConditionResult.UNDEFINED;
574 }
575 }
576 }
577
578
579 ByteString normalizedSubFinal;
580 if (subFinal == null)
581 {
582 normalizedSubFinal = null;
583 }
584 else
585 {
586 try
587 {
588 normalizedSubFinal =
589 matchingRule.normalizeSubstring(subFinal);
590 }
591 catch (Exception e)
592 {
593 if (debugEnabled())
594 {
595 TRACER.debugCaught(DebugLogLevel.ERROR, e);
596 }
597
598 // The substring couldn't be normalized. We have to return
599 // "undefined".
600 return ConditionResult.UNDEFINED;
601 }
602 }
603
604
605 ConditionResult result = ConditionResult.FALSE;
606 for (AttributeValue value : getValues())
607 {
608 try
609 {
610 if (matchingRule.valueMatchesSubstring(
611 value.getNormalizedValue(),
612 normalizedSubInitial,
613 normalizedSubAny,
614 normalizedSubFinal))
615 {
616 return ConditionResult.TRUE;
617 }
618 }
619 catch (Exception e)
620 {
621 if (debugEnabled())
622 {
623 TRACER.debugCaught(DebugLogLevel.ERROR, e);
624 }
625
626 // The value couldn't be normalized. If we can't find a
627 // definite match, then we should return "undefined".
628 result = ConditionResult.UNDEFINED;
629 }
630 }
631
632 return result;
633 }
634
635
636
637 /**
638 * Indicates whether this attribute has any value(s) that are
639 * greater than or equal to the provided value.
640 *
641 * @param value The value for which to make the determination.
642 *
643 * @return <CODE>UNDEFINED</CODE> if this attribute does not have
644 * an ordering matching rule, <CODE>TRUE</CODE> if at least
645 * one value is greater than or equal to the provided
646 * value, or <CODE>false</CODE> otherwise.
647 */
648 public ConditionResult greaterThanOrEqualTo(AttributeValue value)
649 {
650 OrderingMatchingRule matchingRule =
651 attributeType.getOrderingMatchingRule();
652 if (matchingRule == null)
653 {
654 return ConditionResult.UNDEFINED;
655 }
656
657 ByteString normalizedValue;
658 try
659 {
660 normalizedValue = value.getNormalizedValue();
661 }
662 catch (Exception e)
663 {
664 if (debugEnabled())
665 {
666 TRACER.debugCaught(DebugLogLevel.ERROR, e);
667 }
668
669 // We couldn't normalize the provided value. We should return
670 // "undefined".
671 return ConditionResult.UNDEFINED;
672 }
673
674 ConditionResult result = ConditionResult.FALSE;
675 for (AttributeValue v : getValues())
676 {
677 try
678 {
679 ByteString nv = v.getNormalizedValue();
680 int comparisonResult =
681 matchingRule.compareValues(nv, normalizedValue);
682 if (comparisonResult >= 0)
683 {
684 return ConditionResult.TRUE;
685 }
686 }
687 catch (Exception e)
688 {
689 if (debugEnabled())
690 {
691 TRACER.debugCaught(DebugLogLevel.ERROR, e);
692 }
693
694 // We couldn't normalize one of the attribute values. If we
695 // can't find a definite match, then we should return
696 // "undefined".
697 result = ConditionResult.UNDEFINED;
698 }
699 }
700
701 return result;
702 }
703
704
705
706 /**
707 * Indicates whether this attribute has any value(s) that are less
708 * than or equal to the provided value.
709 *
710 * @param value The value for which to make the determination.
711 *
712 * @return <CODE>UNDEFINED</CODE> if this attribute does not have
713 * an ordering matching rule, <CODE>TRUE</CODE> if at least
714 * one value is less than or equal to the provided value,
715 * or <CODE>false</CODE> otherwise.
716 */
717 public ConditionResult lessThanOrEqualTo(AttributeValue value)
718 {
719 OrderingMatchingRule matchingRule =
720 attributeType.getOrderingMatchingRule();
721 if (matchingRule == null)
722 {
723 return ConditionResult.UNDEFINED;
724 }
725
726 ByteString normalizedValue;
727 try
728 {
729 normalizedValue = value.getNormalizedValue();
730 }
731 catch (Exception e)
732 {
733 if (debugEnabled())
734 {
735 TRACER.debugCaught(DebugLogLevel.ERROR, e);
736 }
737
738 // We couldn't normalize the provided value. We should return
739 // "undefined".
740 return ConditionResult.UNDEFINED;
741 }
742
743 ConditionResult result = ConditionResult.FALSE;
744 for (AttributeValue v : getValues())
745 {
746 try
747 {
748 ByteString nv = v.getNormalizedValue();
749 int comparisonResult =
750 matchingRule.compareValues(nv, normalizedValue);
751 if (comparisonResult <= 0)
752 {
753 return ConditionResult.TRUE;
754 }
755 }
756 catch (Exception e)
757 {
758 if (debugEnabled())
759 {
760 TRACER.debugCaught(DebugLogLevel.ERROR, e);
761 }
762
763 // We couldn't normalize one of the attribute values. If we
764 // can't find a definite match, then we should return
765 // "undefined".
766 result = ConditionResult.UNDEFINED;
767 }
768 }
769
770 return result;
771 }
772
773
774
775 /**
776 * Indicates whether this attribute has any value(s) that are
777 * approximately equal to the provided value.
778 *
779 * @param value The value for which to make the determination.
780 *
781 * @return <CODE>UNDEFINED</CODE> if this attribute does not have
782 * an approximate matching rule, <CODE>TRUE</CODE> if at
783 * least one value is approximately equal to the provided
784 * value, or <CODE>false</CODE> otherwise.
785 */
786 public ConditionResult approximatelyEqualTo(AttributeValue value)
787 {
788 ApproximateMatchingRule matchingRule =
789 attributeType.getApproximateMatchingRule();
790 if (matchingRule == null)
791 {
792 return ConditionResult.UNDEFINED;
793 }
794
795 ByteString normalizedValue;
796 try
797 {
798 normalizedValue = matchingRule.normalizeValue(value.getValue());
799 }
800 catch (Exception e)
801 {
802 if (debugEnabled())
803 {
804 TRACER.debugCaught(DebugLogLevel.ERROR, e);
805 }
806
807 // We couldn't normalize the provided value. We should return
808 // "undefined".
809 return ConditionResult.UNDEFINED;
810 }
811
812 ConditionResult result = ConditionResult.FALSE;
813 for (AttributeValue v : getValues())
814 {
815 try
816 {
817 ByteString nv = matchingRule.normalizeValue(v.getValue());
818 if (matchingRule.approximatelyMatch(nv, normalizedValue))
819 {
820 return ConditionResult.TRUE;
821 }
822 }
823 catch (Exception e)
824 {
825 if (debugEnabled())
826 {
827 TRACER.debugCaught(DebugLogLevel.ERROR, e);
828 }
829
830 // We couldn't normalize one of the attribute values. If we
831 // can't find a definite match, then we should return
832 // "undefined".
833 result = ConditionResult.UNDEFINED;
834 }
835 }
836
837 return result;
838 }
839
840
841
842 /**
843 * Indicates whether this is a virtual attribute rather than a real
844 * attribute.
845 *
846 * @return {@code true} if this is a virtual attribute, or
847 * {@code false} if it is a real attribute.
848 */
849 public boolean isVirtual()
850 {
851 return false;
852 }
853
854
855
856 /**
857 * Creates a duplicate of this attribute that can be modified
858 * without impacting this attribute.
859 *
860 * @return A duplicate of this attribute that can be modified
861 * without impacting this attribute.
862 */
863 public Attribute duplicate()
864 {
865 return duplicate(false);
866 }
867
868
869 /**
870 * Creates a duplicate of this attribute that can be modified
871 * without impacting this attribute.
872 *
873 * @param omitValues <CODE>true</CODE> if the values should be
874 * omitted.
875 *
876 * @return A duplicate of this attribute that can be modified
877 * without impacting this attribute.
878 */
879 public Attribute duplicate(boolean omitValues)
880 {
881 LinkedHashSet<String> optionsCopy =
882 new LinkedHashSet<String>(options);
883
884 if (omitValues)
885 {
886 return new Attribute(attributeType, name, optionsCopy, null);
887 }
888 else
889 {
890 LinkedHashSet<AttributeValue> valuesCopy =
891 new LinkedHashSet<AttributeValue>(getValues());
892
893 return new Attribute(attributeType, name, optionsCopy,
894 valuesCopy);
895 }
896 }
897
898
899 /**
900 * Indicates whether the provided object is an attribute that is
901 * equal to this attribute. It will be considered equal if the
902 * attribute type, set of values, and set of options are equal.
903 *
904 * @param o The object for which to make the determination.
905 *
906 * @return <CODE>true</CODE> if the provided object is an attribute
907 * that is equal to this attribute, or <CODE>false</CODE>
908 * if not.
909 */
910 public boolean equals(Object o)
911 {
912 if (this == o)
913 {
914 return true;
915 }
916
917 if ((o == null) || (! (o instanceof Attribute)))
918 {
919 return false;
920 }
921
922 Attribute a = (Attribute) o;
923 if (! attributeType.equals(a.attributeType))
924 {
925 return false;
926 }
927
928 if (getValues().size() != a.getValues().size())
929 {
930 return false;
931 }
932
933 if (! hasAllValues(a.getValues()))
934 {
935 return false;
936 }
937
938 return optionsEqual(a.options);
939 }
940
941
942
943 /**
944 * Retrieves the hash code for this attribute. It will be
945 * calculated as the sum of the hash code for the attribute type and
946 * all values.
947 *
948 * @return The hash code for this attribute.
949 */
950 public int hashCode()
951 {
952 int hashCode = attributeType.hashCode();
953 for (AttributeValue value : getValues())
954 {
955 hashCode += value.hashCode();
956 }
957
958 return hashCode;
959 }
960
961
962
963 /**
964 * Retrieves a one-line string representation of this attribute.
965 *
966 * @return A one-line string representation of this attribute.
967 */
968 public String toString()
969 {
970 StringBuilder buffer = new StringBuilder();
971 toString(buffer);
972 return buffer.toString();
973 }
974
975
976
977 /**
978 * Appends a one-line string representation of this attribute to the
979 * provided buffer.
980 *
981 * @param buffer The buffer to which the information should be
982 * appended.
983 */
984 public void toString(StringBuilder buffer)
985 {
986 buffer.append("Attribute(");
987 buffer.append(name);
988 buffer.append(", {");
989
990 boolean firstValue = true;
991 for (AttributeValue value : getValues())
992 {
993 if (! firstValue)
994 {
995 buffer.append(", ");
996 }
997
998 value.toString(buffer);
999 firstValue = false;
1000 }
1001
1002 buffer.append("})");
1003 }
1004
1005
1006
1007 /**
1008 * Retrieves a string representation of this attribute in LDIF form.
1009 *
1010 * @return A string representation of this attribute in LDIF form.
1011 */
1012 public String toLDIF()
1013 {
1014 StringBuilder buffer = new StringBuilder();
1015 toLDIF(buffer);
1016 return buffer.toString();
1017 }
1018
1019
1020
1021 /**
1022 * Appends a string representation of this attribute in LDIF form to
1023 * the provided buffer.
1024 *
1025 * @param buffer The buffer to which the information should be
1026 * appended.
1027 */
1028 public void toLDIF(StringBuilder buffer)
1029 {
1030 for (AttributeValue value : getValues())
1031 {
1032 buffer.append(name);
1033
1034 if (needsBase64Encoding(value.getValueBytes()))
1035 {
1036 buffer.append("::");
1037 buffer.append(Base64.encode(value.getValueBytes()));
1038 }
1039 else
1040 {
1041 buffer.append(": ");
1042 buffer.append(value.getStringValue());
1043 }
1044
1045 buffer.append(EOL);
1046 }
1047 }
1048 }
1049