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