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