001/* 002 * Copyright 2018-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2018-2019 Ping Identity Corporation 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.Comparator; 027 028import com.unboundid.asn1.ASN1OctetString; 029import com.unboundid.ldap.sdk.schema.Schema; 030import com.unboundid.util.NotMutable; 031import com.unboundid.util.StaticUtils; 032import com.unboundid.util.ThreadSafety; 033import com.unboundid.util.ThreadSafetyLevel; 034 035 036 037/** 038 * This class provides a data structure that represents a single name-value pair 039 * that may appear in a relative distinguished name. 040 */ 041@NotMutable() 042@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 043public final class RDNNameValuePair 044 implements Comparable<RDNNameValuePair>, Comparator<RDNNameValuePair>, 045 Serializable 046{ 047 /** 048 * The serial version UID for this serializable class. 049 */ 050 private static final long serialVersionUID = -8780852504883527870L; 051 052 053 054 // The attribute value for this name-value pair. 055 private final ASN1OctetString attributeValue; 056 057 // The schema to use to generate the normalized string representation of this 058 // name-value pair, if any. 059 private final Schema schema; 060 061 // The attribute name for this name-value pair. 062 private final String attributeName; 063 064 // The all-lowercase representation of the attribute name for this name-value 065 // pair. 066 private volatile String lowerAttributeName; 067 068 // The normalized string representation for this RDN name-value pair. 069 private volatile String normalizedString; 070 071 // The string representation for this RDN name-value pair. 072 private volatile String stringRepresentation; 073 074 075 076 /** 077 * Creates a new RDN name-value pair with the provided information. 078 * 079 * @param attributeName The attribute name for this name-value pair. It must 080 * not be {@code null}. 081 * @param attributeValue The attribute value for this name-value pair. It 082 * must not be {@code null}. 083 * @param schema The schema to use to generate the normalized string 084 * representation of this name-value pair, if any. It 085 * may be {@code null} if no schema is available. 086 */ 087 RDNNameValuePair(final String attributeName, 088 final ASN1OctetString attributeValue, final Schema schema) 089 { 090 this.attributeName = attributeName; 091 this.attributeValue = attributeValue; 092 this.schema = schema; 093 094 lowerAttributeName = null; 095 normalizedString = null; 096 stringRepresentation = null; 097 } 098 099 100 101 /** 102 * Retrieves the attribute name for this name-value pair. 103 * 104 * @return The attribute name for this name-value pair. 105 */ 106 public String getAttributeName() 107 { 108 return attributeName; 109 } 110 111 112 113 /** 114 * Retrieves an all-lowercase representation of the attribute name. 115 * 116 * @return An all-lowercase representation of the attribute name. 117 */ 118 String getLowercaseAttributeName() 119 { 120 if (lowerAttributeName == null) 121 { 122 lowerAttributeName = StaticUtils.toLowerCase(attributeName); 123 } 124 125 return lowerAttributeName; 126 } 127 128 129 130 /** 131 * Retrieves the string representation of the attribute value for this 132 * name-value pair. 133 * 134 * @return The string representation of the attribute value for this 135 * name-value pair. 136 */ 137 public String getAttributeValue() 138 { 139 return attributeValue.stringValue(); 140 } 141 142 143 144 /** 145 * Retrieves the bytes that comprise the attribute value for this name-value 146 * pair. 147 * 148 * @return The bytes that comprise the attribute value for this name-value 149 * pair. 150 */ 151 public byte[] getAttributeValueBytes() 152 { 153 return attributeValue.getValue(); 154 } 155 156 157 158 /** 159 * Retrieves the raw attribute value for this name-value pair. 160 * 161 * @return The raw attribute value for this name-value pair. 162 */ 163 public ASN1OctetString getRawAttributeValue() 164 { 165 return attributeValue; 166 } 167 168 169 170 /** 171 * Retrieves an integer value that represents the order in which this RDN 172 * name-value pair should be placed in relation to the provided RDN name-value 173 * pair in a sorted list. 174 * 175 * @param p The RDN name-value pair to be ordered relative to this RDN 176 * name-value pair. It must not be {@code null}. 177 * 178 * @return A negative integer if this RDN name-value pair should be ordered 179 * before the provided RDN name-value pair, a positive integer if this RDN 180 * name-value pair should be ordered after the provided RDN name-value pair, 181 * or zero if this RDN name-value pair is logically equivalent to the provided 182 * RDN name-value pair. 183 */ 184 @Override() 185 public int compareTo(final RDNNameValuePair p) 186 { 187 final String thisLowerName = getLowercaseAttributeName(); 188 final String thatLowerName = p.getLowercaseAttributeName(); 189 final int nameComparison = thisLowerName.compareTo(thatLowerName); 190 if (nameComparison != 0) 191 { 192 return nameComparison; 193 } 194 195 final String thisNormalizedString = toNormalizedString(); 196 final String thatNormalizedString = p.toNormalizedString(); 197 return thisNormalizedString.compareTo(thatNormalizedString); 198 } 199 200 201 202 /** 203 * Retrieves an integer value that represents the order in which the provided 204 * RDN name-value pairs should be placed in a sorted list. 205 * 206 * @param p1 The first RDN name-value pair to compare. It must not be {@code 207 * null}. 208 * @param p2 The second RDN name-value pair to compare. It must not be {@code 209 * null}. 210 * 211 * @return A negative integer if the first RDN name-value pair should be 212 * ordered before the second RDN name-value pair, a positive integer if the 213 * first RDN name-value pair should be ordered after the second RDN name-value 214 * pair, or zero if the provided RDN name-value pairs are logically 215 * equivalent. 216 */ 217 @Override() 218 public int compare(final RDNNameValuePair p1, final RDNNameValuePair p2) 219 { 220 return p1.compareTo(p2); 221 } 222 223 224 225 /** 226 * Retrieves a hash code for this RDN name-value pair. 227 * 228 * @return A hash code for this RDN name-value pair. 229 */ 230 @Override() 231 public int hashCode() 232 { 233 return toNormalizedString().hashCode(); 234 } 235 236 237 238 /** 239 * Indicates whether the provided object is considered logically equivalent to 240 * this RDN name-value pair. 241 * 242 * @param o The object for which to make the determination. 243 * 244 * @return {@code true} if the provided object is an RDN name-value pair that 245 * is logically equivalent to this RDN name-value pair, or {@code false} if 246 * not. 247 */ 248 public boolean equals(final Object o) 249 { 250 if (o == null) 251 { 252 return false; 253 } 254 255 if (o == this) 256 { 257 return true; 258 } 259 260 if (! (o instanceof RDNNameValuePair)) 261 { 262 return false; 263 } 264 265 final RDNNameValuePair p = (RDNNameValuePair) o; 266 return toNormalizedString().equals(p.toNormalizedString()); 267 } 268 269 270 271 /** 272 * Retrieves a string representation of this RDN name-value pair. 273 * 274 * @return A string representation of this RDN name-value pair. 275 */ 276 @Override() 277 public String toString() 278 { 279 if (stringRepresentation == null) 280 { 281 final StringBuilder buffer = new StringBuilder(); 282 toString(buffer, false); 283 stringRepresentation = buffer.toString(); 284 } 285 286 return stringRepresentation; 287 } 288 289 290 291 /** 292 * Retrieves a string representation of this RDN name-value pair with minimal 293 * encoding for special characters. Only those characters specified in RFC 294 * 4514 section 2.4 will be escaped. No escaping will be used for non-ASCII 295 * characters or non-printable ASCII characters. 296 * 297 * @return A string representation of this RDN name-value pair with minimal 298 * encoding for special characters. 299 */ 300 public String toMinimallyEncodedString() 301 { 302 final StringBuilder buffer = new StringBuilder(); 303 toString(buffer, true); 304 return buffer.toString(); 305 } 306 307 308 309 /** 310 * Appends a string representation of this RDN name-value pair to the provided 311 * buffer. 312 * 313 * @param buffer The buffer to which the string representation is 314 * to be appended. 315 * @param minimizeEncoding Indicates whether to restrict the encoding of 316 * special characters to the bare minimum required 317 * by LDAP (as per RFC 4514 section 2.4). If this 318 * is {@code true}, then only leading and trailing 319 * spaces, double quotes, plus signs, commas, 320 * semicolons, greater-than, less-than, and 321 * backslash characters will be encoded. 322 */ 323 public void toString(final StringBuilder buffer, 324 final boolean minimizeEncoding) 325 { 326 if ((stringRepresentation != null) && (! minimizeEncoding)) 327 { 328 buffer.append(stringRepresentation); 329 return; 330 } 331 332 final boolean bufferWasEmpty = (buffer.length() == 0); 333 334 buffer.append(attributeName); 335 buffer.append('='); 336 RDN.appendValue(buffer, attributeValue, minimizeEncoding); 337 338 if (bufferWasEmpty && (! minimizeEncoding)) 339 { 340 stringRepresentation = buffer.toString(); 341 } 342 } 343 344 345 346 /** 347 * Retrieves a normalized string representation of this RDN name-value pair. 348 * 349 * @return A normalized string representation of this RDN name-value pair. 350 */ 351 public String toNormalizedString() 352 { 353 if (normalizedString == null) 354 { 355 final StringBuilder buffer = new StringBuilder(); 356 toNormalizedString(buffer); 357 normalizedString = buffer.toString(); 358 } 359 360 return normalizedString; 361 } 362 363 364 365 /** 366 * Appends a normalized string representation of this RDN name-value pair to 367 * the provided buffer. 368 * 369 * @param buffer The buffer to which the normalized string representation 370 * should be appended. It must not be {@code null}. 371 */ 372 public void toNormalizedString(final StringBuilder buffer) 373 { 374 buffer.append(getLowercaseAttributeName()); 375 buffer.append('='); 376 RDN.appendNormalizedValue(buffer, attributeName, attributeValue, schema); 377 } 378}