001    /*
002     * Copyright 2009-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-2016 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.ldap.sdk;
022    
023    
024    
025    import java.io.Serializable;
026    import java.util.ArrayList;
027    import java.util.Collection;
028    import java.util.Collections;
029    import java.util.Date;
030    import java.util.Iterator;
031    import java.util.List;
032    import java.util.Set;
033    
034    import com.unboundid.util.ByteStringBuffer;
035    import com.unboundid.util.NotMutable;
036    import com.unboundid.util.ThreadSafety;
037    import com.unboundid.util.ThreadSafetyLevel;
038    
039    import static com.unboundid.util.Validator.*;
040    
041    
042    
043    /**
044     * This class provides a data structure that represents a compact version of an
045     * entry.  This is basically the same as an {@code Entry} object, except that
046     * it stores the information in a more compact form that requires less space in
047     * memory.  This may be useful in applications that need to hold a large number
048     * of entries in memory.  Note that performance of some methods in this class
049     * may be significantly worse than the performance of the corresponding methods
050     * in the {@code Entry} class.
051     *
052     * @see  Entry
053     */
054    @NotMutable()
055    @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
056    public final class CompactEntry
057           implements Serializable
058    {
059      /**
060       * The serial version UID for this serializable class.
061       */
062      private static final long serialVersionUID = 8067151651120794058L;
063    
064    
065    
066      // The set of attributes for this entry.
067      private final CompactAttribute[] attributes;
068    
069      // The hash code for this entry, if it has been calculated.
070      private int hashCode;
071    
072      // The DN for this entry.
073      private final String dn;
074    
075    
076    
077      /**
078       * Creates a new compact entry from the provided entry.
079       *
080       * @param  entry  The entry to use to create this compact entry.  It must not
081       *                be {@code null}.
082       */
083      public CompactEntry(final Entry entry)
084      {
085        ensureNotNull(entry);
086    
087        dn = entry.getDN();
088        hashCode = -1;
089    
090        final Collection<Attribute> attrs = entry.getAttributes();
091        attributes = new CompactAttribute[attrs.size()];
092        final Iterator<Attribute> iterator = attrs.iterator();
093        for (int i=0; i < attributes.length; i++)
094        {
095          attributes[i] = new CompactAttribute(iterator.next());
096        }
097      }
098    
099    
100    
101      /**
102       * Retrieves the DN for this entry.
103       *
104       * @return  The DN for this entry.
105       */
106      public String getDN()
107      {
108        return dn;
109      }
110    
111    
112    
113      /**
114       * Retrieves the parsed DN for this entry.
115       *
116       * @return  The parsed DN for this entry.
117       *
118       * @throws  LDAPException  If the DN string cannot be parsed as a valid DN.
119       */
120      public DN getParsedDN()
121             throws LDAPException
122      {
123        return new DN(dn);
124      }
125    
126    
127    
128      /**
129       * Retrieves the RDN for this entry.
130       *
131       * @return  The RDN for this entry, or {@code null} if the DN is the null DN.
132       *
133       * @throws  LDAPException  If the DN string cannot be parsed as a valid DN.
134       */
135      public RDN getRDN()
136             throws LDAPException
137      {
138        return getParsedDN().getRDN();
139      }
140    
141    
142    
143      /**
144       * Retrieves the parent DN for this entry.
145       *
146       * @return  The parent DN for this entry, or {@code null} if there is no
147       *          parent.
148       *
149       * @throws  LDAPException  If the DN string cannot be parsed as a valid DN.
150       */
151      public DN getParentDN()
152             throws LDAPException
153      {
154        return getParsedDN().getParent();
155      }
156    
157    
158    
159      /**
160       * Retrieves the parent DN for this entry as a string.
161       *
162       * @return  The parent DN for this entry as a string, or {@code null} if there
163       *          is no parent.
164       *
165       * @throws  LDAPException  If the DN string cannot be parsed as a valid DN.
166       */
167      public String getParentDNString()
168             throws LDAPException
169      {
170        return getParsedDN().getParentString();
171      }
172    
173    
174    
175      /**
176       * Indicates whether this entry contains the specified attribute.
177       *
178       * @param  attributeName  The name of the attribute for which to make the
179       *                        determination.  It must not be {@code null}.
180       *
181       * @return  {@code true} if this entry contains the specified attribute, or
182       *          {@code false} if not.
183       */
184      public boolean hasAttribute(final String attributeName)
185      {
186        ensureNotNull(attributeName);
187    
188        for (final CompactAttribute a : attributes)
189        {
190          if (a.getName().equalsIgnoreCase(attributeName))
191          {
192            return true;
193          }
194        }
195    
196        return false;
197      }
198    
199    
200    
201      /**
202       * Indicates whether this entry contains the specified attribute.  It will
203       * only return {@code true} if this entry contains an attribute with the same
204       * name and exact set of values.
205       *
206       * @param  attribute  The attribute for which to make the determination.  It
207       *                    must not be {@code null}.
208       *
209       * @return  {@code true} if this entry contains the specified attribute, or
210       *          {@code false}.
211       */
212      public boolean hasAttribute(final Attribute attribute)
213      {
214        ensureNotNull(attribute);
215    
216        for (final CompactAttribute a : attributes)
217        {
218          if (a.toAttribute().equals(attribute))
219          {
220            return true;
221          }
222        }
223    
224        return false;
225      }
226    
227    
228    
229      /**
230       * Indicates whether this entry contains an attribute with the given name and
231       * value.
232       *
233       * @param  attributeName   The name of the attribute for which to make the
234       *                         determination.  It must not be {@code null}.
235       * @param  attributeValue  The value for which to make the determination.  It
236       *                         must not be {@code null}.
237       *
238       * @return  {@code true} if this entry contains an attribute with the
239       *          specified name and value, or {@code false} if not.
240       */
241      public boolean hasAttributeValue(final String attributeName,
242                                       final String attributeValue)
243      {
244        ensureNotNull(attributeName, attributeValue);
245    
246        for (final CompactAttribute a : attributes)
247        {
248          if (a.getName().equalsIgnoreCase(attributeName) &&
249              a.toAttribute().hasValue(attributeValue))
250          {
251            return true;
252          }
253        }
254    
255        return false;
256      }
257    
258    
259    
260      /**
261       * Indicates whether this entry contains an attribute with the given name and
262       * value.
263       *
264       * @param  attributeName   The name of the attribute for which to make the
265       *                         determination.  It must not be {@code null}.
266       * @param  attributeValue  The value for which to make the determination.  It
267       *                         must not be {@code null}.
268       *
269       * @return  {@code true} if this entry contains an attribute with the
270       *          specified name and value, or {@code false} if not.
271       */
272      public boolean hasAttributeValue(final String attributeName,
273                                       final byte[] attributeValue)
274      {
275        ensureNotNull(attributeName, attributeValue);
276    
277        for (final CompactAttribute a : attributes)
278        {
279          if (a.getName().equalsIgnoreCase(attributeName) &&
280              a.toAttribute().hasValue(attributeValue))
281          {
282            return true;
283          }
284        }
285    
286        return false;
287      }
288    
289    
290    
291      /**
292       * Indicates whether this entry contains the specified object class.
293       *
294       * @param  objectClassName  The name of the object class for which to make the
295       *                          determination.  It must not be {@code null}.
296       *
297       * @return  {@code true} if this entry contains the specified object class, or
298       *          {@code false} if not.
299       */
300      public boolean hasObjectClass(final String objectClassName)
301      {
302        return hasAttributeValue("objectClass", objectClassName);
303      }
304    
305    
306    
307      /**
308       * Retrieves the set of attributes contained in this entry.
309       *
310       * @return  The set of attributes contained in this entry.
311       */
312      public Collection<Attribute> getAttributes()
313      {
314        final ArrayList<Attribute> attrList =
315             new ArrayList<Attribute>(attributes.length);
316        for (final CompactAttribute a : attributes)
317        {
318          attrList.add(a.toAttribute());
319        }
320    
321        return Collections.unmodifiableCollection(attrList);
322      }
323    
324    
325    
326      /**
327       * Retrieves the attribute with the specified name.
328       *
329       * @param  attributeName  The name of the attribute to retrieve.  It must not
330       *                        be {@code null}.
331       *
332       * @return  The requested attribute from this entry, or {@code null} if the
333       *          specified attribute is not present in this entry.
334       */
335      public Attribute getAttribute(final String attributeName)
336      {
337        ensureNotNull(attributeName);
338    
339        for (final CompactAttribute a : attributes)
340        {
341          if (a.getName().equalsIgnoreCase(attributeName))
342          {
343            return a.toAttribute();
344          }
345        }
346    
347        return null;
348      }
349    
350    
351    
352      /**
353       * Retrieves the list of attributes with the given base name and all of the
354       * specified options.
355       *
356       * @param  baseName  The base name (without any options) for the attribute to
357       *                   retrieve.  It must not be {@code null}.
358       * @param  options   The set of options that should be included in the
359       *                   attributes that are returned.  It may be empty or
360       *                   {@code null} if all attributes with the specified base
361       *                   name should be returned, regardless of the options that
362       *                   they contain (if any).
363       *
364       * @return  The list of attributes with the given base name and all of the
365       *          specified options.  It may be empty if there are no attributes
366       *          with the specified base name and set of options.
367       */
368      public List<Attribute> getAttributesWithOptions(final String baseName,
369                                                      final Set<String> options)
370      {
371        return toEntry().getAttributesWithOptions(baseName, options);
372      }
373    
374    
375    
376      /**
377       * Retrieves the value for the specified attribute, if available.  If the
378       * attribute has more than one value, then the first value will be returned.
379       *
380       * @param  attributeName  The name of the attribute for which to retrieve the
381       *                        value.  It must not be {@code null}.
382       *
383       * @return  The value for the specified attribute, or {@code null} if that
384       *          attribute is not available.
385       */
386      public String getAttributeValue(final String attributeName)
387      {
388        ensureNotNull(attributeName);
389    
390        for (final CompactAttribute a : attributes)
391        {
392          if (a.getName().equalsIgnoreCase(attributeName))
393          {
394            final String[] values = a.getStringValues();
395            if (values.length > 0)
396            {
397              return values[0];
398            }
399            else
400            {
401              return null;
402            }
403          }
404        }
405    
406        return null;
407      }
408    
409    
410    
411      /**
412       * Retrieves the value for the specified attribute as a byte array, if
413       * available.  If the attribute has more than one value, then the first value
414       * will be returned.
415       *
416       * @param  attributeName  The name of the attribute for which to retrieve the
417       *                        value.  It must not be {@code null}.
418       *
419       * @return  The value for the specified attribute as a byte array, or
420       *          {@code null} if that attribute is not available.
421       */
422      public byte[] getAttributeValueBytes(final String attributeName)
423      {
424        ensureNotNull(attributeName);
425    
426        for (final CompactAttribute a : attributes)
427        {
428          if (a.getName().equalsIgnoreCase(attributeName))
429          {
430            final byte[][] values = a.getByteValues();
431            if (values.length > 0)
432            {
433              return values[0];
434            }
435            else
436            {
437              return null;
438            }
439          }
440        }
441    
442        return null;
443      }
444    
445    
446    
447      /**
448       * Retrieves the value for the specified attribute as a Boolean, if available.
449       * If the attribute has more than one value, then the first value will be
450       * returned.  Values of "true", "t", "yes", "y", "on", and "1" will be
451       * interpreted as {@code TRUE}.  Values of "false", "f", "no", "n", "off", and
452       * "0" will be interpreted as {@code FALSE}.
453       *
454       * @param  attributeName  The name of the attribute for which to retrieve the
455       *                        value.  It must not be {@code null}.
456       *
457       * @return  The Boolean value parsed from the specified attribute, or
458       *          {@code null} if that attribute is not available or the value
459       *          cannot be parsed as a Boolean.
460       */
461      public Boolean getAttributeValueAsBoolean(final String attributeName)
462      {
463        ensureNotNull(attributeName);
464    
465        final Attribute a = getAttribute(attributeName);
466        if (a == null)
467        {
468          return null;
469        }
470        else
471        {
472          return a.getValueAsBoolean();
473        }
474      }
475    
476    
477    
478      /**
479       * Retrieves the value for the specified attribute as a Date, formatted using
480       * the generalized time syntax, if available.  If the attribute has more than
481       * one value, then the first value will be returned.
482       *
483       * @param  attributeName  The name of the attribute for which to retrieve the
484       *                        value.  It must not be {@code null}.
485       *
486       * @return  The Date value parsed from the specified attribute, or
487       *           {@code null} if that attribute is not available or the value
488       *           cannot be parsed as a Date.
489       */
490      public Date getAttributeValueAsDate(final String attributeName)
491      {
492        ensureNotNull(attributeName);
493    
494        final Attribute a = getAttribute(attributeName);
495        if (a == null)
496        {
497          return null;
498        }
499        else
500        {
501          return a.getValueAsDate();
502        }
503      }
504    
505    
506    
507      /**
508       * Retrieves the value for the specified attribute as a DN, if available.  If
509       * the attribute has more than one value, then the first value will be
510       * returned.
511       *
512       * @param  attributeName  The name of the attribute for which to retrieve the
513       *                        value.  It must not be {@code null}.
514       *
515       * @return  The Date value parsed from the specified attribute, or
516       *           {@code null} if that attribute is not available or the value
517       *           cannot be parsed as a DN.
518       */
519      public DN getAttributeValueAsDN(final String attributeName)
520      {
521        ensureNotNull(attributeName);
522    
523        final Attribute a = getAttribute(attributeName);
524        if (a == null)
525        {
526          return null;
527        }
528        else
529        {
530          return a.getValueAsDN();
531        }
532      }
533    
534    
535    
536      /**
537       * Retrieves the value for the specified attribute as an Integer, if
538       * available.  If the attribute has more than one value, then the first value
539       * will be returned.
540       *
541       * @param  attributeName  The name of the attribute for which to retrieve the
542       *                        value.  It must not be {@code null}.
543       *
544       * @return  The Integer value parsed from the specified attribute, or
545       *          {@code null} if that attribute is not available or the value
546       *          cannot be parsed as an Integer.
547       */
548      public Integer getAttributeValueAsInteger(final String attributeName)
549      {
550        ensureNotNull(attributeName);
551    
552        final Attribute a = getAttribute(attributeName);
553        if (a == null)
554        {
555          return null;
556        }
557        else
558        {
559          return a.getValueAsInteger();
560        }
561      }
562    
563    
564    
565      /**
566       * Retrieves the value for the specified attribute as a Long, if available.
567       * If the attribute has more than one value, then the first value will be
568       * returned.
569       *
570       * @param  attributeName  The name of the attribute for which to retrieve the
571       *                        value.  It must not be {@code null}.
572       *
573       * @return  The Long value parsed from the specified attribute, or
574       *          {@code null} if that attribute is not available or the value
575       *          cannot be parsed as a Long.
576       */
577      public Long getAttributeValueAsLong(final String attributeName)
578      {
579        ensureNotNull(attributeName);
580    
581        final Attribute a = getAttribute(attributeName);
582        if (a == null)
583        {
584          return null;
585        }
586        else
587        {
588          return a.getValueAsLong();
589        }
590      }
591    
592    
593    
594      /**
595       * Retrieves the set of values for the specified attribute, if available.
596       *
597       * @param  attributeName  The name of the attribute for which to retrieve the
598       *                        values.  It must not be {@code null}.
599       *
600       * @return  The set of values for the specified attribute, or {@code null} if
601       *          that attribute is not available.
602       */
603      public String[] getAttributeValues(final String attributeName)
604      {
605        ensureNotNull(attributeName);
606    
607        for (final CompactAttribute a : attributes)
608        {
609          if (a.getName().equalsIgnoreCase(attributeName))
610          {
611            return a.getStringValues();
612          }
613        }
614    
615        return null;
616      }
617    
618    
619    
620      /**
621       * Retrieves the set of values for the specified attribute as byte arrays, if
622       * available.
623       *
624       * @param  attributeName  The name of the attribute for which to retrieve the
625       *                        values.  It must not be {@code null}.
626       *
627       * @return  The set of values for the specified attribute as byte arrays, or
628       *          {@code null} if that attribute is not available.
629       */
630      public byte[][] getAttributeValueByteArrays(final String attributeName)
631      {
632        ensureNotNull(attributeName);
633    
634        for (final CompactAttribute a : attributes)
635        {
636          if (a.getName().equalsIgnoreCase(attributeName))
637          {
638            return a.getByteValues();
639          }
640        }
641    
642        return null;
643      }
644    
645    
646    
647      /**
648       * Retrieves the "objectClass" attribute from the entry, if available.
649       *
650       * @return  The "objectClass" attribute from the entry, or {@code null} if
651       *          that attribute not available.
652       */
653      public Attribute getObjectClassAttribute()
654      {
655        return getAttribute("objectClass");
656      }
657    
658    
659    
660      /**
661       * Retrieves the values of the "objectClass" attribute from the entry, if
662       * available.
663       *
664       * @return  The values of the "objectClass" attribute from the entry, or
665       *          {@code null} if that attribute is not available.
666       */
667      public String[] getObjectClassValues()
668      {
669        return getAttributeValues("objectClass");
670      }
671    
672    
673    
674      /**
675       * Converts this compact entry to a full entry.
676       *
677       * @return  The entry created from this compact entry.
678       */
679      public Entry toEntry()
680      {
681        final Attribute[] attrs = new Attribute[attributes.length];
682        for (int i=0; i < attributes.length; i++)
683        {
684          attrs[i] = attributes[i].toAttribute();
685        }
686    
687        return new Entry(dn, attrs);
688      }
689    
690    
691    
692      /**
693       * Generates a hash code for this entry.
694       *
695       * @return  The generated hash code for this entry.
696       */
697      @Override()
698      public int hashCode()
699      {
700        if (hashCode == -1)
701        {
702          hashCode = toEntry().hashCode();
703        }
704    
705        return hashCode;
706      }
707    
708    
709    
710      /**
711       * Indicates whether the provided object is equal to this entry.  The provided
712       * object will only be considered equal to this entry if it is an entry with
713       * the same DN and set of attributes.
714       *
715       * @param  o  The object for which to make the determination.
716       *
717       * @return  {@code true} if the provided object is considered equal to this
718       *          entry, or {@code false} if not.
719       */
720      @Override()
721      public boolean equals(final Object o)
722      {
723        if ((o == null) || (! (o instanceof CompactEntry)))
724        {
725          return false;
726        }
727    
728        return toEntry().equals(((CompactEntry) o).toEntry());
729      }
730    
731    
732    
733      /**
734       * Retrieves an LDIF representation of this entry, with each attribute value
735       * on a separate line.  Long lines will not be wrapped.
736       *
737       * @return  An LDIF representation of this entry.
738       */
739      public String[] toLDIF()
740      {
741        return toLDIF(0);
742      }
743    
744    
745    
746      /**
747       * Retrieves an LDIF representation of this entry, with each attribute value
748       * on a separate line.  Long lines will be wrapped at the specified column.
749       *
750       * @param  wrapColumn  The column at which long lines should be wrapped.  A
751       *                     value less than or equal to two indicates that no
752       *                     wrapping should be performed.
753       *
754       * @return  An LDIF representation of this entry.
755       */
756      public String[] toLDIF(final int wrapColumn)
757      {
758        return toEntry().toLDIF(wrapColumn);
759      }
760    
761    
762    
763      /**
764       * Appends an LDIF representation of this entry to the provided buffer.  Long
765       * lines will not be wrapped.
766       *
767       * @param  buffer The buffer to which the LDIF representation of this entry
768       *                should be written.
769       */
770      public void toLDIF(final ByteStringBuffer buffer)
771      {
772        toLDIF(buffer, 0);
773      }
774    
775    
776    
777      /**
778       * Appends an LDIF representation of this entry to the provided buffer.
779       *
780       * @param  buffer      The buffer to which the LDIF representation of this
781       *                     entry should be written.
782       * @param  wrapColumn  The column at which long lines should be wrapped.  A
783       *                     value less than or equal to two indicates that no
784       *                     wrapping should be performed.
785       */
786      public void toLDIF(final ByteStringBuffer buffer, final int wrapColumn)
787      {
788        toEntry().toLDIF(buffer, wrapColumn);
789      }
790    
791    
792    
793      /**
794       * Retrieves an LDIF-formatted string representation of this entry.  No
795       * wrapping will be performed, and no extra blank lines will be added.
796       *
797       * @return  An LDIF-formatted string representation of this entry.
798       */
799      public String toLDIFString()
800      {
801        final StringBuilder buffer = new StringBuilder();
802        toLDIFString(buffer, 0);
803        return buffer.toString();
804      }
805    
806    
807    
808      /**
809       * Retrieves an LDIF-formatted string representation of this entry.  No
810       * extra blank lines will be added.
811       *
812       * @param  wrapColumn  The column at which long lines should be wrapped.  A
813       *                     value less than or equal to two indicates that no
814       *                     wrapping should be performed.
815       *
816       * @return  An LDIF-formatted string representation of this entry.
817       */
818      public String toLDIFString(final int wrapColumn)
819      {
820        final StringBuilder buffer = new StringBuilder();
821        toLDIFString(buffer, wrapColumn);
822        return buffer.toString();
823      }
824    
825    
826    
827      /**
828       * Appends an LDIF-formatted string representation of this entry to the
829       * provided buffer.  No wrapping will be performed, and no extra blank lines
830       * will be added.
831       *
832       * @param  buffer  The buffer to which to append the LDIF representation of
833       *                 this entry.
834       */
835      public void toLDIFString(final StringBuilder buffer)
836      {
837        toLDIFString(buffer, 0);
838      }
839    
840    
841    
842      /**
843       * Appends an LDIF-formatted string representation of this entry to the
844       * provided buffer.  No extra blank lines will be added.
845       *
846       * @param  buffer      The buffer to which to append the LDIF representation
847       *                     of this entry.
848       * @param  wrapColumn  The column at which long lines should be wrapped.  A
849       *                     value less than or equal to two indicates that no
850       *                     wrapping should be performed.
851       */
852      public void toLDIFString(final StringBuilder buffer,
853                                     final int wrapColumn)
854      {
855        toEntry().toLDIFString(buffer, wrapColumn);
856      }
857    
858    
859    
860      /**
861       * Retrieves a string representation of this entry.
862       *
863       * @return  A string representation of this entry.
864       */
865      @Override()
866      public String toString()
867      {
868        final StringBuilder buffer = new StringBuilder();
869        toString(buffer);
870        return buffer.toString();
871      }
872    
873    
874    
875      /**
876       * Appends a string representation of this entry to the provided buffer.
877       *
878       * @param  buffer  The buffer to which to append the string representation of
879       *                 this entry.
880       */
881      public void toString(final StringBuilder buffer)
882      {
883        buffer.append("Entry(dn='");
884        buffer.append(dn);
885        buffer.append("', attributes={");
886    
887        for (int i=0; i < attributes.length; i++)
888        {
889          if (i > 0)
890          {
891            buffer.append(", ");
892          }
893          attributes[i].toAttribute().toString(buffer);
894        }
895    
896        buffer.append("})");
897      }
898    }