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