001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.api.ldap.model.name; 021 022 023import java.io.Externalizable; 024import java.io.IOException; 025import java.io.ObjectInput; 026import java.io.ObjectOutput; 027import java.util.Arrays; 028 029import org.apache.directory.api.i18n.I18n; 030import org.apache.directory.api.ldap.model.entry.Value; 031import org.apache.directory.api.ldap.model.exception.LdapException; 032import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; 033import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException; 034import org.apache.directory.api.ldap.model.message.ResultCodeEnum; 035import org.apache.directory.api.ldap.model.schema.AttributeType; 036import org.apache.directory.api.ldap.model.schema.LdapComparator; 037import org.apache.directory.api.ldap.model.schema.MatchingRule; 038import org.apache.directory.api.ldap.model.schema.Normalizer; 039import org.apache.directory.api.ldap.model.schema.SchemaManager; 040import org.apache.directory.api.util.Serialize; 041import org.apache.directory.api.util.Strings; 042import org.slf4j.Logger; 043import org.slf4j.LoggerFactory; 044 045 046/** 047 * <p> 048 * A Attribute Type And Value, which is the basis of all Rdn. It contains a 049 * type, and a value. The type must not be case sensitive. Superfluous leading 050 * and trailing spaces MUST have been trimmed before. The value MUST be in UTF8 051 * format, according to RFC 2253. If the type is in OID form, then the value 052 * must be a hexadecimal string prefixed by a '#' character. Otherwise, the 053 * string must respect the RC 2253 grammar. 054 * </p> 055 * <p> 056 * We will also keep a User Provided form of the AVA (Attribute Type And Value), 057 * called upName. 058 * </p> 059 * <p> 060 * This class is immutable 061 * </p> 062 * 063 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 064 */ 065public class Ava implements Externalizable, Cloneable, Comparable<Ava> 066{ 067 /** 068 * Declares the Serial Version Uid. 069 * 070 * @see <a 071 * href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always 072 * Declare Serial Version Uid</a> 073 */ 074 private static final long serialVersionUID = 1L; 075 076 /** The LoggerFactory used by this class */ 077 private static final Logger LOG = LoggerFactory.getLogger( Ava.class ); 078 079 /** The normalized Name type */ 080 private String normType; 081 082 /** The user provided Name type */ 083 private String upType; 084 085 /** The value. It can be a String or a byte array */ 086 private Value value; 087 088 /** The user provided Ava */ 089 private String upName; 090 091 /** The attributeType if the Ava is schemaAware */ 092 private AttributeType attributeType; 093 094 /** the schema manager */ 095 private transient SchemaManager schemaManager; 096 097 /** The computed hashcode */ 098 private volatile int h; 099 100 101 /** 102 * Constructs an empty Ava 103 */ 104 public Ava() 105 { 106 this( null ); 107 } 108 109 110 /** 111 * Constructs an empty schema aware Ava. 112 * 113 * @param schemaManager The SchemaManager instance 114 */ 115 public Ava( SchemaManager schemaManager ) 116 { 117 normType = null; 118 upType = null; 119 value = null; 120 upName = ""; 121 this.schemaManager = schemaManager; 122 attributeType = null; 123 } 124 125 126 /** 127 * Constructs new Ava using the provided SchemaManager and AVA 128 * 129 * @param schemaManager The SchemaManager instance 130 * @param ava The AVA to copy 131 * @throws LdapInvalidDnException If the Ava is invalid 132 */ 133 public Ava( SchemaManager schemaManager, Ava ava ) throws LdapInvalidDnException 134 { 135 upType = ava.upType; 136 this.schemaManager = schemaManager; 137 138 if ( ava.isSchemaAware() ) 139 { 140 normType = ava.normType; 141 value = ava.value; 142 attributeType = ava.getAttributeType(); 143 } 144 else 145 { 146 if ( schemaManager != null ) 147 { 148 attributeType = schemaManager.getAttributeType( ava.normType ); 149 150 if ( attributeType != null ) 151 { 152 normType = attributeType.getOid(); 153 154 try 155 { 156 value = new Value( attributeType, ava.value ); 157 } 158 catch ( LdapInvalidAttributeValueException e ) 159 { 160 throw new LdapInvalidDnException( e.getResultCode() ); 161 } 162 } 163 else 164 { 165 normType = ava.normType; 166 value = ava.value; 167 } 168 } 169 else 170 { 171 normType = ava.normType; 172 value = ava.value; 173 } 174 } 175 176 upName = getEscaped(); 177 178 hashCode(); 179 } 180 181 182 /** 183 * Construct an Ava containing a binary value. 184 * <p> 185 * Note that the upValue should <b>not</b> be null or empty, or resolve 186 * to an empty string after having trimmed it. 187 * 188 * @param upType The User Provided type 189 * @param upValue The User Provided binary value 190 * 191 * @throws LdapInvalidDnException If the given type or value are invalid 192 */ 193 public Ava( String upType, byte[] upValue ) throws LdapInvalidDnException 194 { 195 this( null, upType, upValue ); 196 } 197 198 199 /** 200 * Construct a schema aware Ava containing a binary value. The AttributeType 201 * and value will be normalized accordingly to the given SchemaManager. 202 * <p> 203 * Note that the upValue should <b>not</b> be null or empty, or resolve 204 * to an empty string after having trimmed it. 205 * 206 * @param schemaManager The SchemaManager instance 207 * @param upType The User Provided type 208 * @param upValue The User Provided binary value 209 * 210 * @throws LdapInvalidDnException If the given type or value are invalid 211 */ 212 public Ava( SchemaManager schemaManager, String upType, byte[] upValue ) throws LdapInvalidDnException 213 { 214 if ( schemaManager != null ) 215 { 216 this.schemaManager = schemaManager; 217 218 try 219 { 220 attributeType = schemaManager.lookupAttributeTypeRegistry( upType ); 221 } 222 catch ( LdapException le ) 223 { 224 String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY ); 225 // Do NOT log the message here. The error may be handled and therefore the log message may polute the log files. 226 // Let the caller log the exception if needed. 227 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le ); 228 } 229 230 try 231 { 232 createAva( schemaManager, upType, new Value( attributeType, upValue ) ); 233 } 234 catch ( LdapInvalidAttributeValueException liave ) 235 { 236 String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY ); 237 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave ); 238 } 239 } 240 else 241 { 242 createAva( upType, new Value( upValue ) ); 243 } 244 } 245 246 247 /** 248 * Construct a schema aware Ava containing a binary value. The AttributeType 249 * and value will be normalized accordingly to the given SchemaManager. 250 * <p> 251 * Note that the upValue should <b>not</b> be null or empty, or resolve 252 * to an empty string after having trimmed it. 253 * 254 * @param schemaManager The SchemaManager instance 255 * @param upType The User Provided type 256 * @param upName the User Provided AVA 257 * @param upValue The User Provided binary value 258 * 259 * @throws LdapInvalidDnException If the given type or value are invalid 260 */ 261 public Ava( SchemaManager schemaManager, String upType, String upName, byte[] upValue ) throws LdapInvalidDnException 262 { 263 if ( schemaManager != null ) 264 { 265 this.schemaManager = schemaManager; 266 267 try 268 { 269 attributeType = schemaManager.lookupAttributeTypeRegistry( upType ); 270 } 271 catch ( LdapException le ) 272 { 273 String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY ); 274 // Do NOT log the message here. The error may be handled and therefore the log message may polute the log files. 275 // Let the caller log the exception if needed. 276 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le ); 277 } 278 279 try 280 { 281 createAva( schemaManager, upType, new Value( attributeType, upValue ) ); 282 } 283 catch ( LdapInvalidAttributeValueException liave ) 284 { 285 String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY ); 286 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave ); 287 } 288 } 289 else 290 { 291 createAva( upType, new Value( upValue ) ); 292 } 293 294 this.upName = upName; 295 } 296 297 298 /** 299 * Construct an Ava with a String value. 300 * <p> 301 * Note that the upValue should <b>not</b> be null or empty, or resolve 302 * to an empty string after having trimmed it. 303 * 304 * @param upType The User Provided type 305 * @param upValue The User Provided String value 306 * 307 * @throws LdapInvalidDnException If the given type or value are invalid 308 */ 309 public Ava( String upType, String upValue ) throws LdapInvalidDnException 310 { 311 this( null, upType, upValue ); 312 } 313 314 315 /** 316 * Construct a schema aware Ava with a String value. 317 * <p> 318 * Note that the upValue should <b>not</b> be null or empty, or resolve 319 * to an empty string after having trimmed it. 320 * 321 * @param schemaManager The SchemaManager instance 322 * @param upType The User Provided type 323 * @param upValue The User Provided String value 324 * 325 * @throws LdapInvalidDnException If the given type or value are invalid 326 */ 327 public Ava( SchemaManager schemaManager, String upType, String upValue ) throws LdapInvalidDnException 328 { 329 if ( schemaManager != null ) 330 { 331 this.schemaManager = schemaManager; 332 333 try 334 { 335 attributeType = schemaManager.lookupAttributeTypeRegistry( upType ); 336 } 337 catch ( LdapException le ) 338 { 339 String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY ); 340 // Do NOT log the message here. The error may be handled and therefore the log message may polute the log files. 341 // Let the caller log the exception if needed. 342 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le ); 343 } 344 345 try 346 { 347 createAva( schemaManager, upType, new Value( attributeType, upValue ) ); 348 } 349 catch ( LdapInvalidAttributeValueException liave ) 350 { 351 String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY ); 352 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave ); 353 } 354 } 355 else 356 { 357 createAva( upType, new Value( upValue ) ); 358 } 359 } 360 361 362 /** 363 * Construct a schema aware Ava with a String value. 364 * <p> 365 * Note that the upValue should <b>not</b> be null or empty, or resolve 366 * to an empty string after having trimmed it. 367 * 368 * @param schemaManager The SchemaManager instance 369 * @param upType The User Provided type 370 * @param upName the User provided AVA 371 * @param upValue The User Provided String value 372 * 373 * @throws LdapInvalidDnException If the given type or value are invalid 374 */ 375 public Ava( SchemaManager schemaManager, String upType, String upName, String upValue ) throws LdapInvalidDnException 376 { 377 if ( schemaManager != null ) 378 { 379 this.schemaManager = schemaManager; 380 381 try 382 { 383 attributeType = schemaManager.lookupAttributeTypeRegistry( upType ); 384 } 385 catch ( LdapException le ) 386 { 387 String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY ); 388 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le ); 389 } 390 391 try 392 { 393 createAva( schemaManager, upType, new Value( attributeType, upValue ) ); 394 } 395 catch ( LdapInvalidAttributeValueException liave ) 396 { 397 String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY ); 398 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, liave ); 399 } 400 } 401 else 402 { 403 createAva( upType, new Value( upValue ) ); 404 } 405 406 this.upName = upName; 407 } 408 409 410 /** 411 * Construct an Ava. The type and value are normalized : 412 * <ul> 413 * <li> the type is trimmed and lowercased </li> 414 * <li> the value is trimmed </li> 415 * </ul> 416 * <p> 417 * Note that the upValue should <b>not</b> be null or empty, or resolved 418 * to an empty string after having trimmed it. 419 * 420 * @param upType The User Provided type 421 * @param normType The normalized type 422 * @param value The User Provided value 423 * @param upName The User Provided name (may be escaped) 424 * 425 * @throws LdapInvalidDnException If the given type or value are invalid 426 */ 427 // WARNING : The protection level is left unspecified intentionally. 428 // We need this method to be visible from the DnParser class, but not 429 // from outside this package. 430 /* Unspecified protection */Ava( String upType, String normType, Value value, String upName ) 431 throws LdapInvalidDnException 432 { 433 this( null, upType, normType, value, upName ); 434 } 435 436 437 /** 438 * Construct an Ava. The type and value are normalized : 439 * <ul> 440 * <li> the type is trimmed and lowercased </li> 441 * <li> the value is trimmed </li> 442 * </ul> 443 * <p> 444 * Note that the upValue should <b>not</b> be null or empty, or resolved 445 * to an empty string after having trimmed it. 446 * 447 * @param attributeType The AttributeType for this value 448 * @param upType The User Provided type 449 * @param normType The normalized type 450 * @param value The value 451 * @param upName The User Provided name (may be escaped) 452 * 453 * @throws LdapInvalidDnException If the given type or value are invalid 454 */ 455 // WARNING : The protection level is left unspecified intentionally. 456 // We need this method to be visible from the DnParser class, but not 457 // from outside this package. 458 /* Unspecified protection */Ava( AttributeType attributeType, String upType, String normType, Value value, String upName ) 459 throws LdapInvalidDnException 460 { 461 this.attributeType = attributeType; 462 String upTypeTrimmed = Strings.trim( upType ); 463 String normTypeTrimmed = Strings.trim( normType ); 464 465 if ( Strings.isEmpty( upTypeTrimmed ) ) 466 { 467 if ( Strings.isEmpty( normTypeTrimmed ) ) 468 { 469 String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY ); 470 LOG.error( message ); 471 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message ); 472 } 473 else 474 { 475 // In this case, we will use the normType instead 476 this.normType = Strings.lowerCaseAscii( normTypeTrimmed ); 477 this.upType = normType; 478 } 479 } 480 else if ( Strings.isEmpty( normTypeTrimmed ) ) 481 { 482 // In this case, we will use the upType instead 483 this.normType = Strings.lowerCaseAscii( upTypeTrimmed ); 484 this.upType = upType; 485 } 486 else 487 { 488 this.normType = Strings.lowerCaseAscii( normTypeTrimmed ); 489 this.upType = upType; 490 } 491 492 this.value = value; 493 this.upName = upName; 494 hashCode(); 495 } 496 497 498 /** 499 * Construct an Ava. The type and value are normalized : 500 * <ul> 501 * <li> the type is trimmed and lowercased </li> 502 * <li> the value is trimmed </li> 503 * </ul> 504 * <p> 505 * Note that the upValue should <b>not</b> be null or empty, or resolved 506 * to an empty string after having trimmed it. 507 * 508 * @param schemaManager The SchemaManager 509 * @param upType The User Provided type 510 * @param normType The normalized type 511 * @param value The value 512 * 513 * @throws LdapInvalidDnException If the given type or value are invalid 514 */ 515 // WARNING : The protection level is left unspecified intentionally. 516 // We need this method to be visible from the DnParser class, but not 517 // from outside this package. 518 /* Unspecified protection */Ava( SchemaManager schemaManager, String upType, String normType, Value value ) 519 throws LdapInvalidDnException 520 { 521 StringBuilder sb = new StringBuilder(); 522 523 this.upType = upType; 524 this.normType = normType; 525 this.value = value; 526 527 sb.append( upType ); 528 sb.append( '=' ); 529 530 if ( ( value != null ) && ( value.getString() != null ) ) 531 { 532 sb.append( value.getString() ); 533 } 534 535 upName = sb.toString(); 536 537 if ( schemaManager != null ) 538 { 539 apply( schemaManager ); 540 } 541 542 hashCode(); 543 } 544 545 546 /** 547 * Construct a schema aware Ava. The AttributeType and value will be checked accordingly 548 * to the SchemaManager. 549 * <p> 550 * Note that the upValue should <b>not</b> be null or empty, or resolve 551 * to an empty string after having trimmed it. 552 * 553 * @param schemaManager The SchemaManager instance 554 * @param upType The User Provided type 555 * @param value The value 556 */ 557 private void createAva( SchemaManager schemaManager, String upType, Value value ) 558 { 559 StringBuilder sb = new StringBuilder(); 560 561 normType = attributeType.getOid(); 562 this.upType = upType; 563 this.value = value; 564 565 sb.append( upType ); 566 sb.append( '=' ); 567 568 if ( value != null ) 569 { 570 sb.append( Rdn.escapeValue( value.getString() ) ); 571 } 572 573 upName = sb.toString(); 574 575 hashCode(); 576 } 577 578 579 /** 580 * Construct an Ava. The type and value are normalized : 581 * <ul> 582 * <li> the type is trimmed and lowercased </li> 583 * <li> the value is trimmed </li> 584 * </ul> 585 * <p> 586 * Note that the upValue should <b>not</b> be null or empty, or resolved 587 * to an empty string after having trimmed it. 588 * 589 * @param upType The User Provided type 590 * @param upValue The User Provided value 591 * 592 * @throws LdapInvalidDnException If the given type or value are invalid 593 */ 594 private void createAva( String upType, Value upValue ) throws LdapInvalidDnException 595 { 596 String upTypeTrimmed = Strings.trim( upType ); 597 String normTypeTrimmed = Strings.trim( normType ); 598 599 if ( Strings.isEmpty( upTypeTrimmed ) ) 600 { 601 if ( Strings.isEmpty( normTypeTrimmed ) ) 602 { 603 String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY ); 604 // Do NOT log the message here. The error may be handled and therefore the log message may polute the log files. 605 // Let the caller log the exception if needed. 606 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message ); 607 } 608 else 609 { 610 // In this case, we will use the normType instead 611 this.normType = Strings.lowerCaseAscii( normTypeTrimmed ); 612 this.upType = normType; 613 } 614 } 615 else if ( Strings.isEmpty( normTypeTrimmed ) ) 616 { 617 // In this case, we will use the upType instead 618 this.normType = Strings.lowerCaseAscii( upTypeTrimmed ); 619 this.upType = upType; 620 } 621 else 622 { 623 this.normType = Strings.lowerCaseAscii( normTypeTrimmed ); 624 this.upType = upType; 625 626 } 627 628 value = upValue; 629 630 upName = getEscaped(); 631 632 hashCode(); 633 } 634 635 636 /** 637 * Apply a SchemaManager to the Ava. It will normalize the Ava.<br> 638 * If the Ava already had a SchemaManager, then the new SchemaManager will be 639 * used instead. 640 * 641 * @param schemaManager The SchemaManager instance to use 642 * @throws LdapInvalidDnException If the Ava can't be normalized accordingly 643 * to the given SchemaManager 644 */ 645 private void apply( SchemaManager schemaManager ) throws LdapInvalidDnException 646 { 647 if ( schemaManager != null ) 648 { 649 this.schemaManager = schemaManager; 650 651 AttributeType tmpAttributeType = null; 652 653 try 654 { 655 tmpAttributeType = schemaManager.lookupAttributeTypeRegistry( normType ); 656 } 657 catch ( LdapException le ) 658 { 659 if ( schemaManager.isRelaxed() ) 660 { 661 // No attribute in the schema, but the schema is relaxed : get out 662 return; 663 } 664 else 665 { 666 String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY ); 667 // Do NOT log the message here. The error may be handled and therefore the log message may polute the log files. 668 // Let the caller log the exception if needed. 669 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le ); 670 } 671 } 672 673 if ( this.attributeType == tmpAttributeType ) 674 { 675 // No need to normalize again 676 return; 677 } 678 else 679 { 680 this.attributeType = tmpAttributeType; 681 } 682 683 try 684 { 685 value = new Value( tmpAttributeType, value ); 686 } 687 catch ( LdapException le ) 688 { 689 String message = I18n.err( I18n.ERR_13600_TYPE_IS_NULL_OR_EMPTY ); 690 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, message, le ); 691 } 692 693 hashCode(); 694 } 695 } 696 697 698 /** 699 * Get the normalized type of a Ava 700 * 701 * @return The normalized type 702 */ 703 public String getNormType() 704 { 705 return normType; 706 } 707 708 709 /** 710 * Get the user provided type of a Ava 711 * 712 * @return The user provided type 713 */ 714 public String getType() 715 { 716 return upType; 717 } 718 719 720 /** 721 * Get the Value of a Ava 722 * 723 * @return The value 724 */ 725 public Value getValue() 726 { 727 return value.clone(); 728 } 729 730 731 /** 732 * Get the user provided form of this attribute type and value 733 * 734 * @return The user provided form of this ava 735 */ 736 public String getName() 737 { 738 return upName; 739 } 740 741 742 /** 743 * @return The Ava as an escaped String 744 */ 745 public String getEscaped() 746 { 747 StringBuilder sb = new StringBuilder(); 748 749 sb.append( getType() ); 750 sb.append( '=' ); 751 sb.append( value.getEscaped() ); 752 753 return sb.toString(); 754 } 755 756 757 /** 758 * Implements the cloning. 759 * 760 * @return a clone of this object 761 */ 762 @Override 763 public Ava clone() 764 { 765 try 766 { 767 Ava clone = ( Ava ) super.clone(); 768 clone.value = value.clone(); 769 770 return clone; 771 } 772 catch ( CloneNotSupportedException cnse ) 773 { 774 throw new Error( I18n.err( I18n.ERR_13621_ASSERTION_FAILURE ), cnse ); 775 } 776 } 777 778 779 /** 780 * Gets the hashcode of this object. 781 * 782 * @see java.lang.Object#hashCode() 783 * @return The instance hash code 784 */ 785 @Override 786 public int hashCode() 787 { 788 if ( h == 0 ) 789 { 790 int hTmp = 37; 791 792 hTmp = hTmp * 17 + ( normType != null ? normType.hashCode() : 0 ); 793 h = hTmp * 17 + ( value != null ? value.hashCode() : 0 ); 794 } 795 796 return h; 797 } 798 799 800 /** 801 * @see Object#equals(Object) 802 */ 803 @Override 804 public boolean equals( Object obj ) 805 { 806 if ( this == obj ) 807 { 808 return true; 809 } 810 811 if ( !( obj instanceof Ava ) ) 812 { 813 return false; 814 } 815 816 Ava instance = ( Ava ) obj; 817 818 // Compare the type 819 if ( attributeType == null ) 820 { 821 if ( normType == null ) 822 { 823 if ( instance.normType != null ) 824 { 825 return false; 826 } 827 } 828 else 829 { 830 if ( !normType.equals( instance.normType ) ) 831 { 832 return false; 833 } 834 } 835 } 836 else 837 { 838 if ( instance.getAttributeType() == null ) 839 { 840 if ( ( schemaManager != null ) 841 && !attributeType.equals( schemaManager.getAttributeType( instance.getType() ) ) ) 842 { 843 return false; 844 } 845 } 846 else if ( !attributeType.equals( instance.getAttributeType() ) ) 847 { 848 return false; 849 } 850 } 851 852 // Compare the values 853 if ( ( value == null ) || value.isNull() ) 854 { 855 return ( instance.value == null ) || instance.value.isNull(); 856 } 857 else 858 { 859 if ( schemaManager != null ) 860 { 861 if ( ( value.getString() != null ) && value.getString().equals( instance.value.getString() ) ) 862 { 863 return true; 864 } 865 866 if ( attributeType == null ) 867 { 868 attributeType = schemaManager.getAttributeType( normType ); 869 } 870 871 MatchingRule equalityMatchingRule = attributeType.getEquality(); 872 873 if ( equalityMatchingRule != null ) 874 { 875 Normalizer normalizer = equalityMatchingRule.getNormalizer(); 876 877 try 878 { 879 return equalityMatchingRule.getLdapComparator().compare( normalizer.normalize( value.getString() ), 880 instance.value.getString() ) == 0; 881 } 882 catch ( LdapException le ) 883 { 884 // TODO: is this OK? If the comparison is not reliable without normalization then we should throw exception 885 // instead returning false. Returning false may be misleading and the log message can be easily overlooked. 886 // If the comparison is reliable, this should not really be an error. Maybe use debug or trace instead? 887 LOG.error( I18n.err( I18n.ERR_13620_CANNOT_NORMALIZE_VALUE ), le.getMessage() ); 888 return false; 889 } 890 } 891 else 892 { 893 // No Equality MR, use a direct comparison 894 if ( !value.isHumanReadable() ) 895 { 896 return Arrays.equals( value.getBytes(), instance.value.getBytes() ); 897 } 898 else 899 { 900 return value.getString().equals( instance.value.getString() ); 901 } 902 } 903 } 904 else 905 { 906 return value.equals( instance.value ); 907 } 908 } 909 } 910 911 912 /** 913 * Serialize the AVA into a buffer at the given position. 914 * 915 * @param buffer The buffer which will contain the serialized Ava 916 * @param pos The position in the buffer for the serialized value 917 * @return The new position in the buffer 918 * @throws IOException Id the serialization failed 919 */ 920 public int serialize( byte[] buffer, int pos ) throws IOException 921 { 922 if ( Strings.isEmpty( upName ) 923 || Strings.isEmpty( upType ) 924 || Strings.isEmpty( normType ) 925 || ( value.isNull() ) ) 926 { 927 String message; 928 929 if ( Strings.isEmpty( upName ) ) 930 { 931 message = I18n.err( I18n.ERR_13616_CANNOT_SERIALIZE_AVA_UPNAME_NULL ); 932 } 933 else if ( Strings.isEmpty( upType ) ) 934 { 935 message = I18n.err( I18n.ERR_13617_CANNOT_SERIALIZE_AVA_UPTYPE_NULL ); 936 } 937 else if ( Strings.isEmpty( normType ) ) 938 { 939 message = I18n.err( I18n.ERR_13618_CANNOT_SERIALIZE_AVA_NORMTYPE_NULL ); 940 } 941 else 942 { 943 message = I18n.err( I18n.ERR_13619_CANNOT_SERIALIZE_AVA_VALUE_NULL ); 944 } 945 946 LOG.error( message ); 947 throw new IOException( message ); 948 } 949 950 int length = 0; 951 952 // The upName 953 byte[] upNameBytes = null; 954 955 if ( upName != null ) 956 { 957 upNameBytes = Strings.getBytesUtf8( upName ); 958 length += 1 + 4 + upNameBytes.length; 959 } 960 961 // The upType 962 byte[] upTypeBytes = null; 963 964 if ( upType != null ) 965 { 966 upTypeBytes = Strings.getBytesUtf8( upType ); 967 length += 1 + 4 + upTypeBytes.length; 968 } 969 970 // Is HR 971 length++; 972 973 // The hash code 974 length += 4; 975 976 // Check that we will be able to store the data in the buffer 977 if ( buffer.length - pos < length ) 978 { 979 throw new ArrayIndexOutOfBoundsException(); 980 } 981 982 // Write the upName 983 if ( upName != null ) 984 { 985 buffer[pos++] = Serialize.TRUE; 986 pos = Serialize.serialize( upNameBytes, buffer, pos ); 987 } 988 else 989 { 990 buffer[pos++] = Serialize.FALSE; 991 } 992 993 // Write the upType 994 if ( upType != null ) 995 { 996 buffer[pos++] = Serialize.TRUE; 997 pos = Serialize.serialize( upTypeBytes, buffer, pos ); 998 } 999 else 1000 { 1001 buffer[pos++] = Serialize.FALSE; 1002 } 1003 1004 // Write the isHR flag 1005 if ( value.isHumanReadable() ) 1006 { 1007 buffer[pos++] = Serialize.TRUE; 1008 } 1009 else 1010 { 1011 buffer[pos++] = Serialize.FALSE; 1012 } 1013 1014 // Write the upValue 1015 if ( value.isHumanReadable() ) 1016 { 1017 pos = value.serialize( buffer, pos ); 1018 } 1019 1020 // Write the hash code 1021 pos = Serialize.serialize( h, buffer, pos ); 1022 1023 return pos; 1024 } 1025 1026 1027 /** 1028 * Deserialize an AVA from a byte[], starting at a given position 1029 * 1030 * @param buffer The buffer containing the AVA 1031 * @param pos The position in the buffer 1032 * @return The new position 1033 * @throws IOException If the serialized value is not an AVA 1034 * @throws LdapInvalidAttributeValueException If the serialized AVA is invalid 1035 */ 1036 public int deserialize( byte[] buffer, int pos ) throws IOException, LdapInvalidAttributeValueException 1037 { 1038 if ( ( pos < 0 ) || ( pos >= buffer.length ) ) 1039 { 1040 throw new ArrayIndexOutOfBoundsException(); 1041 } 1042 1043 // Read the upName value, if it's not null 1044 boolean hasUpName = Serialize.deserializeBoolean( buffer, pos ); 1045 pos++; 1046 1047 if ( hasUpName ) 1048 { 1049 byte[] wrappedValueBytes = Serialize.deserializeBytes( buffer, pos ); 1050 pos += 4 + wrappedValueBytes.length; 1051 upName = Strings.utf8ToString( wrappedValueBytes ); 1052 } 1053 1054 // Read the upType value, if it's not null 1055 boolean hasUpType = Serialize.deserializeBoolean( buffer, pos ); 1056 pos++; 1057 1058 if ( hasUpType ) 1059 { 1060 byte[] upTypeBytes = Serialize.deserializeBytes( buffer, pos ); 1061 pos += 4 + upTypeBytes.length; 1062 upType = Strings.utf8ToString( upTypeBytes ); 1063 } 1064 1065 // Update the AtributeType 1066 if ( schemaManager != null ) 1067 { 1068 if ( !Strings.isEmpty( upType ) ) 1069 { 1070 attributeType = schemaManager.getAttributeType( upType ); 1071 } 1072 else 1073 { 1074 attributeType = schemaManager.getAttributeType( normType ); 1075 } 1076 } 1077 1078 if ( attributeType != null ) 1079 { 1080 normType = attributeType.getOid(); 1081 } 1082 else 1083 { 1084 normType = upType; 1085 } 1086 1087 // Read the isHR flag 1088 boolean isHR = Serialize.deserializeBoolean( buffer, pos ); 1089 pos++; 1090 1091 if ( isHR ) 1092 { 1093 // Read the upValue 1094 value = Value.createValue( attributeType ); 1095 pos = value.deserialize( buffer, pos ); 1096 } 1097 1098 // Read the hashCode 1099 h = Serialize.deserializeInt( buffer, pos ); 1100 pos += 4; 1101 1102 return pos; 1103 } 1104 1105 1106 /** 1107 * 1108 * An Ava is composed of a type and a value. 1109 * The data are stored following the structure : 1110 * <ul> 1111 * <li> 1112 * <b>upName</b> The User provided ATAV 1113 * </li> 1114 * <li> 1115 * <b>start</b> The position of this ATAV in the Dn 1116 * </li> 1117 * <li> 1118 * <b>length</b> The ATAV length 1119 * </li> 1120 * <li> 1121 * <b>upType</b> The user Provided Type 1122 * </li> 1123 * <li> 1124 * <b>normType</b> The normalized AttributeType 1125 * </li> 1126 * <li> 1127 * <b>isHR</b> Tells if the value is a String or not 1128 * </li> 1129 * </ul> 1130 * <br> 1131 * if the value is a String : 1132 * <ul> 1133 * <li> 1134 * <b>value</b> The value 1135 * </li> 1136 * </ul> 1137 * <br> 1138 * if the value is binary : 1139 * <ul> 1140 * <li> 1141 * <b>valueLength</b> 1142 * </li> 1143 * <li> 1144 * <b>value</b> The value 1145 * </li> 1146 * </ul> 1147 * 1148 * @see Externalizable#readExternal(ObjectInput) 1149 * 1150 * @throws IOException If the Ava can't be written in the stream 1151 */ 1152 @Override 1153 public void writeExternal( ObjectOutput out ) throws IOException 1154 { 1155 if ( Strings.isEmpty( upName ) 1156 || Strings.isEmpty( upType ) 1157 || Strings.isEmpty( normType ) 1158 || ( value.isNull() ) ) 1159 { 1160 String message; 1161 1162 if ( Strings.isEmpty( upName ) ) 1163 { 1164 message = I18n.err( I18n.ERR_13616_CANNOT_SERIALIZE_AVA_UPNAME_NULL ); 1165 } 1166 else if ( Strings.isEmpty( upType ) ) 1167 { 1168 message = I18n.err( I18n.ERR_13617_CANNOT_SERIALIZE_AVA_UPTYPE_NULL ); 1169 } 1170 else if ( Strings.isEmpty( normType ) ) 1171 { 1172 message = I18n.err( I18n.ERR_13618_CANNOT_SERIALIZE_AVA_NORMTYPE_NULL ); 1173 } 1174 else 1175 { 1176 message = I18n.err( I18n.ERR_13619_CANNOT_SERIALIZE_AVA_VALUE_NULL ); 1177 } 1178 1179 LOG.error( message ); 1180 throw new IOException( message ); 1181 } 1182 1183 if ( upName != null ) 1184 { 1185 out.writeBoolean( true ); 1186 out.writeUTF( upName ); 1187 } 1188 else 1189 { 1190 out.writeBoolean( false ); 1191 } 1192 1193 if ( upType != null ) 1194 { 1195 out.writeBoolean( true ); 1196 out.writeUTF( upType ); 1197 } 1198 else 1199 { 1200 out.writeBoolean( false ); 1201 } 1202 1203 if ( normType != null ) 1204 { 1205 out.writeBoolean( true ); 1206 out.writeUTF( normType ); 1207 } 1208 else 1209 { 1210 out.writeBoolean( false ); 1211 } 1212 1213 boolean isHR = value.isHumanReadable(); 1214 1215 out.writeBoolean( isHR ); 1216 1217 value.writeExternal( out ); 1218 1219 // Write the hashCode 1220 out.writeInt( h ); 1221 1222 out.flush(); 1223 } 1224 1225 1226 /** 1227 * We read back the data to create a new ATAV. The structure 1228 * read is exposed in the {@link Ava#writeExternal(ObjectOutput)} 1229 * method 1230 * 1231 * @see Externalizable#readExternal(ObjectInput) 1232 * 1233 * @throws IOException If the Ava can't b written to the stream 1234 * @throws ClassNotFoundException If we can't deserialize an Ava from the stream 1235 */ 1236 @Override 1237 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException 1238 { 1239 boolean hasUpName = in.readBoolean(); 1240 1241 if ( hasUpName ) 1242 { 1243 upName = in.readUTF(); 1244 } 1245 1246 boolean hasUpType = in.readBoolean(); 1247 1248 if ( hasUpType ) 1249 { 1250 upType = in.readUTF(); 1251 } 1252 1253 boolean hasNormType = in.readBoolean(); 1254 1255 if ( hasNormType ) 1256 { 1257 normType = in.readUTF(); 1258 } 1259 1260 if ( schemaManager != null ) 1261 { 1262 if ( !Strings.isEmpty( upType ) ) 1263 { 1264 attributeType = schemaManager.getAttributeType( upType ); 1265 } 1266 else 1267 { 1268 attributeType = schemaManager.getAttributeType( normType ); 1269 } 1270 } 1271 1272 in.readBoolean(); 1273 1274 value = Value.deserialize( attributeType, in ); 1275 1276 h = in.readInt(); 1277 } 1278 1279 1280 /** 1281 * Tells if the Ava is schema aware or not. 1282 * 1283 * @return <tt>true</tt> if the Ava is schema aware 1284 */ 1285 public boolean isSchemaAware() 1286 { 1287 return attributeType != null; 1288 } 1289 1290 1291 /** 1292 * @return the attributeType 1293 */ 1294 public AttributeType getAttributeType() 1295 { 1296 return attributeType; 1297 } 1298 1299 1300 private int compareValues( Ava that ) 1301 { 1302 int comp; 1303 1304 if ( value.isHumanReadable() ) 1305 { 1306 comp = value.compareTo( that.value ); 1307 1308 return comp; 1309 } 1310 else 1311 { 1312 byte[] bytes1 = value.getBytes(); 1313 byte[] bytes2 = that.value.getBytes(); 1314 1315 for ( int pos = 0; pos < bytes1.length; pos++ ) 1316 { 1317 int v1 = bytes1[pos] & 0x00FF; 1318 int v2 = bytes2[pos] & 0x00FF; 1319 1320 if ( v1 > v2 ) 1321 { 1322 return 1; 1323 } 1324 else if ( v2 > v1 ) 1325 { 1326 return -1; 1327 } 1328 } 1329 1330 return 0; 1331 } 1332 1333 } 1334 1335 1336 /** 1337 * @see Comparable#compareTo(Object) 1338 */ 1339 @Override 1340 public int compareTo( Ava that ) 1341 { 1342 if ( that == null ) 1343 { 1344 return 1; 1345 } 1346 1347 int comp; 1348 1349 if ( schemaManager == null ) 1350 { 1351 // Compare the ATs 1352 comp = normType.compareTo( that.normType ); 1353 1354 if ( comp != 0 ) 1355 { 1356 return comp; 1357 } 1358 1359 // and compare the values 1360 if ( value == null ) 1361 { 1362 if ( that.value == null ) 1363 { 1364 return 0; 1365 } 1366 else 1367 { 1368 return -1; 1369 } 1370 } 1371 else 1372 { 1373 if ( that.value == null ) 1374 { 1375 return 1; 1376 } 1377 else 1378 { 1379 comp = value.compareTo( ( Value ) that.value ); 1380 1381 return comp; 1382 } 1383 } 1384 } 1385 else 1386 { 1387 if ( that.schemaManager == null ) 1388 { 1389 // Problem : we will apply the current Ava SchemaManager to the given Ava 1390 try 1391 { 1392 that.apply( schemaManager ); 1393 } 1394 catch ( LdapInvalidDnException lide ) 1395 { 1396 return 1; 1397 } 1398 } 1399 1400 // First compare the AT OID 1401 comp = attributeType.getOid().compareTo( that.attributeType.getOid() ); 1402 1403 if ( comp != 0 ) 1404 { 1405 return comp; 1406 } 1407 1408 // Now, compare the two values using the ordering matchingRule comparator, if any 1409 MatchingRule orderingMR = attributeType.getOrdering(); 1410 1411 if ( orderingMR != null ) 1412 { 1413 LdapComparator<Object> comparator = ( LdapComparator<Object> ) orderingMR.getLdapComparator(); 1414 1415 if ( comparator != null ) 1416 { 1417 comp = value.compareTo( that.value ); 1418 1419 return comp; 1420 } 1421 else 1422 { 1423 comp = compareValues( that ); 1424 1425 return comp; 1426 } 1427 } 1428 else 1429 { 1430 comp = compareValues( that ); 1431 1432 return comp; 1433 } 1434 } 1435 } 1436 1437 1438 /** 1439 * A String representation of an Ava, as provided by the user. 1440 * 1441 * @return A string representing an Ava 1442 */ 1443 @Override 1444 public String toString() 1445 { 1446 return upName; 1447 } 1448}