001/* 002 * Copyright 2018-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2018-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) 2018-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.Comparator; 042 043import com.unboundid.asn1.ASN1OctetString; 044import com.unboundid.ldap.matchingrules.MatchingRule; 045import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition; 046import com.unboundid.ldap.sdk.schema.Schema; 047import com.unboundid.util.Debug; 048import com.unboundid.util.NotMutable; 049import com.unboundid.util.StaticUtils; 050import com.unboundid.util.ThreadSafety; 051import com.unboundid.util.ThreadSafetyLevel; 052 053 054 055/** 056 * This class provides a data structure that represents a single name-value pair 057 * that may appear in a relative distinguished name. 058 */ 059@NotMutable() 060@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 061public final class RDNNameValuePair 062 implements Comparable<RDNNameValuePair>, Comparator<RDNNameValuePair>, 063 Serializable 064{ 065 /** 066 * The serial version UID for this serializable class. 067 */ 068 private static final long serialVersionUID = -8780852504883527870L; 069 070 071 072 // The attribute value for this name-value pair. 073 private final ASN1OctetString attributeValue; 074 075 // The schema to use to generate the normalized string representation of this 076 // name-value pair, if any. 077 private final Schema schema; 078 079 // The attribute name for this name-value pair. 080 private final String attributeName; 081 082 // The all-lowercase representation of the attribute name for this name-value 083 // pair. 084 private volatile String normalizedAttributeName; 085 086 // The normalized string representation for this RDN name-value pair. 087 private volatile String normalizedString; 088 089 // The string representation for this RDN name-value pair. 090 private volatile String stringRepresentation; 091 092 093 094 /** 095 * Creates a new RDN name-value pair with the provided information. 096 * 097 * @param attributeName The attribute name for this name-value pair. It 098 * must not be {@code null}. 099 * @param attributeValue The attribute value for this name-value pair. It 100 * must not be {@code null}. 101 * @param schema The schema to use to generate the normalized string 102 * representation of this name-value pair, if any. It 103 * may be {@code null} if no schema is available. 104 */ 105 RDNNameValuePair(final String attributeName, 106 final ASN1OctetString attributeValue, final Schema schema) 107 { 108 this.attributeName = attributeName; 109 this.attributeValue = attributeValue; 110 this.schema = schema; 111 112 normalizedAttributeName = null; 113 normalizedString = null; 114 stringRepresentation = null; 115 } 116 117 118 119 /** 120 * Retrieves the attribute name for this name-value pair. 121 * 122 * @return The attribute name for this name-value pair. 123 */ 124 public String getAttributeName() 125 { 126 return attributeName; 127 } 128 129 130 131 /** 132 * Retrieves a normalized representation of the attribute name. 133 * 134 * @return A normalized representation of the attribute name. 135 */ 136 public String getNormalizedAttributeName() 137 { 138 if (normalizedAttributeName == null) 139 { 140 if (schema != null) 141 { 142 final AttributeTypeDefinition attributeType = 143 schema.getAttributeType(attributeName); 144 if (attributeType != null) 145 { 146 normalizedAttributeName = 147 StaticUtils.toLowerCase(attributeType.getNameOrOID()); 148 } 149 } 150 151 if (normalizedAttributeName == null) 152 { 153 normalizedAttributeName = StaticUtils.toLowerCase(attributeName); 154 } 155 } 156 157 return normalizedAttributeName; 158 } 159 160 161 162 /** 163 * Indicates whether this RDN name-value pair has the provided attribute name 164 * (or a name that is logically equivalent to it). 165 * 166 * @param name The name for which to make the determination. 167 * 168 * @return {@code true} if this name-value pair has the provided attribute 169 * name (or a name that is logically equivalent to it), or 170 * {@code false} if not. 171 */ 172 public boolean hasAttributeName(final String name) 173 { 174 if (attributeName.equalsIgnoreCase(name)) 175 { 176 return true; 177 } 178 179 if (schema != null) 180 { 181 final AttributeTypeDefinition attributeType = 182 schema.getAttributeType(attributeName); 183 return ((attributeType != null) && attributeType.hasNameOrOID(name)); 184 } 185 186 return false; 187 } 188 189 190 191 /** 192 * Retrieves the string representation of the attribute value for this 193 * name-value pair. 194 * 195 * @return The string representation of the attribute value for this 196 * name-value pair. 197 */ 198 public String getAttributeValue() 199 { 200 return attributeValue.stringValue(); 201 } 202 203 204 205 /** 206 * Retrieves the bytes that comprise the attribute value for this name-value 207 * pair. 208 * 209 * @return The bytes that comprise the attribute value for this name-value 210 * pair. 211 */ 212 public byte[] getAttributeValueBytes() 213 { 214 return attributeValue.getValue(); 215 } 216 217 218 219 /** 220 * Retrieves the raw attribute value for this name-value pair. 221 * 222 * @return The raw attribute value for this name-value pair. 223 */ 224 public ASN1OctetString getRawAttributeValue() 225 { 226 return attributeValue; 227 } 228 229 230 231 /** 232 * Indicates whether this RDN name-value pair has the provided attribute value 233 * (or a value that is logically equivalent to it). 234 * 235 * @param value The value for which to make the determination. 236 * 237 * @return {@code true} if this RDN name-value pair has the provided 238 * attribute value (or a value that is logically equivalent to it), 239 * or {@code false} if not. 240 */ 241 public boolean hasAttributeValue(final String value) 242 { 243 try 244 { 245 final MatchingRule matchingRule = 246 MatchingRule.selectEqualityMatchingRule(attributeName, schema); 247 return matchingRule.valuesMatch(new ASN1OctetString(value), 248 attributeValue); 249 } 250 catch (final Exception e) 251 { 252 Debug.debugException(e); 253 return false; 254 } 255 } 256 257 258 259 /** 260 * Indicates whether this RDN name-value pair has the provided attribute value 261 * (or a value that is logically equivalent to it). 262 * 263 * @param value The value for which to make the determination. 264 * 265 * @return {@code true} if this RDN name-value pair has the provided 266 * attribute value (or a value that is logically equivalent to it), 267 * or {@code false} if not. 268 */ 269 public boolean hasAttributeValue(final byte[] value) 270 { 271 try 272 { 273 final MatchingRule matchingRule = 274 MatchingRule.selectEqualityMatchingRule(attributeName, schema); 275 return matchingRule.valuesMatch(new ASN1OctetString(value), 276 attributeValue); 277 } 278 catch (final Exception e) 279 { 280 Debug.debugException(e); 281 return false; 282 } 283 } 284 285 286 287 /** 288 * Retrieves an integer value that represents the order in which this RDN 289 * name-value pair should be placed in relation to the provided RDN name-value 290 * pair in a sorted list. 291 * 292 * @param p The RDN name-value pair to be ordered relative to this RDN 293 * name-value pair. It must not be {@code null}. 294 * 295 * @return A negative integer if this RDN name-value pair should be ordered 296 * before the provided RDN name-value pair, a positive integer if 297 * this RDN name-value pair should be ordered after the provided RDN 298 * name-value pair, or zero if this RDN name-value pair is logically 299 * equivalent to the provided RDN name-value pair. 300 */ 301 @Override() 302 public int compareTo(final RDNNameValuePair p) 303 { 304 final String thisNormalizedName = getNormalizedAttributeName(); 305 final String thatNormalizedName = p.getNormalizedAttributeName(); 306 final int nameComparison = 307 thisNormalizedName.compareTo(thatNormalizedName); 308 if (nameComparison != 0) 309 { 310 return nameComparison; 311 } 312 313 try 314 { 315 final MatchingRule matchingRule = 316 MatchingRule.selectOrderingMatchingRule(attributeName, schema); 317 return matchingRule.compareValues(attributeValue, p.attributeValue); 318 } 319 catch (final Exception e) 320 { 321 Debug.debugException(e); 322 323 final String thisNormalizedString = toNormalizedString(); 324 final String thatNormalizedString = p.toNormalizedString(); 325 return thisNormalizedString.compareTo(thatNormalizedString); 326 } 327 } 328 329 330 331 /** 332 * Retrieves an integer value that represents the order in which the provided 333 * RDN name-value pairs should be placed in a sorted list. 334 * 335 * @param p1 The first RDN name-value pair to compare. It must not be 336 * {@code null}. 337 * @param p2 The second RDN name-value pair to compare. It must not be 338 * {@code null}. 339 * 340 * @return A negative integer if the first RDN name-value pair should be 341 * ordered before the second RDN name-value pair, a positive integer 342 * if the first RDN name-value pair should be ordered after the 343 * second RDN name-value pair, or zero if the provided RDN name-value 344 * pairs are logically equivalent. 345 */ 346 @Override() 347 public int compare(final RDNNameValuePair p1, final RDNNameValuePair p2) 348 { 349 return p1.compareTo(p2); 350 } 351 352 353 354 /** 355 * Retrieves a hash code for this RDN name-value pair. 356 * 357 * @return A hash code for this RDN name-value pair. 358 */ 359 @Override() 360 public int hashCode() 361 { 362 return toNormalizedString().hashCode(); 363 } 364 365 366 367 /** 368 * Indicates whether the provided object is considered logically equivalent to 369 * this RDN name-value pair. 370 * 371 * @param o The object for which to make the determination. 372 * 373 * @return {@code true} if the provided object is an RDN name-value pair that 374 * is logically equivalent to this RDN name-value pair, or 375 * {@code false} if not. 376 */ 377 public boolean equals(final Object o) 378 { 379 if (o == null) 380 { 381 return false; 382 } 383 384 if (o == this) 385 { 386 return true; 387 } 388 389 if (! (o instanceof RDNNameValuePair)) 390 { 391 return false; 392 } 393 394 final RDNNameValuePair p = (RDNNameValuePair) o; 395 return toNormalizedString().equals(p.toNormalizedString()); 396 } 397 398 399 400 /** 401 * Retrieves a string representation of this RDN name-value pair. 402 * 403 * @return A string representation of this RDN name-value pair. 404 */ 405 @Override() 406 public String toString() 407 { 408 if (stringRepresentation == null) 409 { 410 final StringBuilder buffer = new StringBuilder(); 411 toString(buffer, false); 412 stringRepresentation = buffer.toString(); 413 } 414 415 return stringRepresentation; 416 } 417 418 419 420 /** 421 * Retrieves a string representation of this RDN name-value pair with minimal 422 * encoding for special characters. Only those characters specified in RFC 423 * 4514 section 2.4 will be escaped. No escaping will be used for non-ASCII 424 * characters or non-printable ASCII characters. 425 * 426 * @return A string representation of this RDN name-value pair with minimal 427 * encoding for special characters. 428 */ 429 public String toMinimallyEncodedString() 430 { 431 final StringBuilder buffer = new StringBuilder(); 432 toString(buffer, true); 433 return buffer.toString(); 434 } 435 436 437 438 /** 439 * Appends a string representation of this RDN name-value pair to the provided 440 * buffer. 441 * 442 * @param buffer The buffer to which the string representation is 443 * to be appended. 444 * @param minimizeEncoding Indicates whether to restrict the encoding of 445 * special characters to the bare minimum required 446 * by LDAP (as per RFC 4514 section 2.4). If this 447 * is {@code true}, then only leading and trailing 448 * spaces, double quotes, plus signs, commas, 449 * semicolons, greater-than, less-than, and 450 * backslash characters will be encoded. 451 */ 452 public void toString(final StringBuilder buffer, 453 final boolean minimizeEncoding) 454 { 455 if ((stringRepresentation != null) && (! minimizeEncoding)) 456 { 457 buffer.append(stringRepresentation); 458 return; 459 } 460 461 final boolean bufferWasEmpty = (buffer.length() == 0); 462 463 buffer.append(attributeName); 464 buffer.append('='); 465 RDN.appendValue(buffer, attributeValue, minimizeEncoding); 466 467 if (bufferWasEmpty && (! minimizeEncoding)) 468 { 469 stringRepresentation = buffer.toString(); 470 } 471 } 472 473 474 475 /** 476 * Retrieves a normalized string representation of this RDN name-value pair. 477 * 478 * @return A normalized string representation of this RDN name-value pair. 479 */ 480 public String toNormalizedString() 481 { 482 if (normalizedString == null) 483 { 484 final StringBuilder buffer = new StringBuilder(); 485 toNormalizedString(buffer); 486 normalizedString = buffer.toString(); 487 } 488 489 return normalizedString; 490 } 491 492 493 494 /** 495 * Appends a normalized string representation of this RDN name-value pair to 496 * the provided buffer. 497 * 498 * @param buffer The buffer to which the normalized string representation 499 * should be appended. It must not be {@code null}. 500 */ 501 public void toNormalizedString(final StringBuilder buffer) 502 { 503 buffer.append(getNormalizedAttributeName()); 504 buffer.append('='); 505 RDN.appendNormalizedValue(buffer, attributeName, attributeValue, schema); 506 } 507}