001/*
002 * Copyright 2009-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2009-2017 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 */
021package com.unboundid.ldap.sdk;
022
023
024
025import java.io.Serializable;
026import java.util.ArrayList;
027import java.util.Collection;
028import java.util.Collections;
029import java.util.Date;
030import java.util.Iterator;
031import java.util.List;
032import java.util.Set;
033
034import com.unboundid.util.ByteStringBuffer;
035import com.unboundid.util.NotMutable;
036import com.unboundid.util.ThreadSafety;
037import com.unboundid.util.ThreadSafetyLevel;
038
039import 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)
056public 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}