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 * http://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 751 if ( value == null ) 752 { 753 return sb.toString(); 754 } 755 756 byte[] bytes = value.getBytes(); 757 758 if ( Strings.isEmpty( bytes ) ) 759 { 760 return sb.toString(); 761 } 762 763 boolean leadChar = true; 764 765 for ( int pos = 0; pos < bytes.length; pos++ ) 766 { 767 boolean trailChar = pos == bytes.length - 1; 768 byte b = bytes[pos]; 769 770 switch ( b ) 771 { 772 case 0x00 : 773 sb.append( "\\00" ); 774 break; 775 776 case 0x01 : 777 case 0x02 : 778 case 0x03 : 779 case 0x04 : 780 case 0x05 : 781 case 0x06 : 782 case 0x07 : 783 case 0x08 : 784 case 0x09 : 785 case 0x0A : 786 case 0x0B : 787 case 0x0C : 788 case 0x0D : 789 case 0x0E : 790 case 0x0F : 791 case 0x10 : 792 case 0x11 : 793 case 0x12 : 794 case 0x13 : 795 case 0x14 : 796 case 0x15 : 797 case 0x16 : 798 case 0x17 : 799 case 0x18 : 800 case 0x19 : 801 case 0x1A : 802 case 0x1B : 803 case 0x1C : 804 case 0x1D : 805 case 0x1E : 806 case 0x1F : 807 sb.append( ( char ) b ); 808 break; 809 810 case 0x20 : 811 if ( leadChar || trailChar ) 812 { 813 sb.append( "\\ " ); 814 } 815 else 816 { 817 sb.append( ( char ) b ); 818 } 819 820 break; 821 822 case 0x21 : 823 sb.append( ( char ) b ); 824 break; 825 826 827 case 0x22 : 828 sb.append( "\\\"" ); 829 break; 830 831 case 0x23 : 832 if ( leadChar ) 833 { 834 sb.append( "\\#" ); 835 } 836 else 837 { 838 sb.append( '#' ); 839 } 840 841 break; 842 843 case 0x24 : 844 case 0x25 : 845 case 0x26 : 846 case 0x27 : 847 case 0x28 : 848 case 0x29 : 849 case 0x2A : 850 sb.append( ( char ) b ); 851 break; 852 853 case 0x2B : 854 sb.append( "\\+" ); 855 break; 856 857 case 0x2C : 858 sb.append( "\\," ); 859 break; 860 861 case 0x2D : 862 case 0x2E : 863 case 0x2F : 864 case 0x30 : 865 case 0x31 : 866 case 0x32 : 867 case 0x33 : 868 case 0x34 : 869 case 0x35 : 870 case 0x36 : 871 case 0x37 : 872 case 0x38 : 873 case 0x39 : 874 case 0x3A : 875 sb.append( ( char ) b ); 876 break; 877 878 case 0x3B : 879 sb.append( "\\;" ); 880 break; 881 882 case 0x3C : 883 sb.append( "\\<" ); 884 break; 885 886 case 0x3D : 887 sb.append( ( char ) b ); 888 break; 889 890 case 0x3E : 891 sb.append( "\\>" ); 892 break; 893 894 case 0x3F : 895 case 0x40 : 896 case 0x41 : 897 case 0x42 : 898 case 0x43 : 899 case 0x44 : 900 case 0x45 : 901 case 0x46 : 902 case 0x47 : 903 case 0x48 : 904 case 0x49 : 905 case 0x4A : 906 case 0x4B : 907 case 0x4C : 908 case 0x4D : 909 case 0x4E : 910 case 0x4F : 911 case 0x50 : 912 case 0x51 : 913 case 0x52 : 914 case 0x53 : 915 case 0x54 : 916 case 0x55 : 917 case 0x56 : 918 case 0x57 : 919 case 0x58 : 920 case 0x59 : 921 case 0x5A : 922 case 0x5B : 923 sb.append( ( char ) b ); 924 break; 925 926 case 0x5C : 927 sb.append( "\\\\" ); 928 break; 929 930 case 0x5D : 931 case 0x5E : 932 case 0x5F : 933 case 0x60 : 934 case 0x61 : 935 case 0x62 : 936 case 0x63 : 937 case 0x64 : 938 case 0x65 : 939 case 0x66 : 940 case 0x67 : 941 case 0x68 : 942 case 0x69 : 943 case 0x6A : 944 case 0x6B : 945 case 0x6C : 946 case 0x6D : 947 case 0x6E : 948 case 0x6F : 949 case 0x70 : 950 case 0x71 : 951 case 0x72 : 952 case 0x73 : 953 case 0x74 : 954 case 0x75 : 955 case 0x76 : 956 case 0x77 : 957 case 0x78 : 958 case 0x79 : 959 case 0x7A : 960 case 0x7B : 961 case 0x7C : 962 case 0x7D : 963 case 0x7E : 964 case 0x7F : 965 sb.append( ( char ) b ); 966 break; 967 968 // Between 0x80 and 0xC1, this is an octet 969 case ( byte ) 0x80 : 970 case ( byte ) 0x81 : 971 case ( byte ) 0x82 : 972 case ( byte ) 0x83 : 973 case ( byte ) 0x84 : 974 case ( byte ) 0x85 : 975 case ( byte ) 0x86 : 976 case ( byte ) 0x87 : 977 case ( byte ) 0x88 : 978 case ( byte ) 0x89 : 979 case ( byte ) 0x8A : 980 case ( byte ) 0x8B : 981 case ( byte ) 0x8C : 982 case ( byte ) 0x8D : 983 case ( byte ) 0x8E : 984 case ( byte ) 0x8F : 985 case ( byte ) 0x90 : 986 case ( byte ) 0x91 : 987 case ( byte ) 0x92 : 988 case ( byte ) 0x93 : 989 case ( byte ) 0x94 : 990 case ( byte ) 0x95 : 991 case ( byte ) 0x96 : 992 case ( byte ) 0x97 : 993 case ( byte ) 0x98 : 994 case ( byte ) 0x99 : 995 case ( byte ) 0x9A : 996 case ( byte ) 0x9B : 997 case ( byte ) 0x9C : 998 case ( byte ) 0x9D : 999 case ( byte ) 0x9E : 1000 case ( byte ) 0x9F : 1001 case ( byte ) 0xA0 : 1002 case ( byte ) 0xA1 : 1003 case ( byte ) 0xA2 : 1004 case ( byte ) 0xA3 : 1005 case ( byte ) 0xA4 : 1006 case ( byte ) 0xA5 : 1007 case ( byte ) 0xA6 : 1008 case ( byte ) 0xA7 : 1009 case ( byte ) 0xA8 : 1010 case ( byte ) 0xA9 : 1011 case ( byte ) 0xAA : 1012 case ( byte ) 0xAB : 1013 case ( byte ) 0xAC : 1014 case ( byte ) 0xAD : 1015 case ( byte ) 0xAE : 1016 case ( byte ) 0xAF : 1017 case ( byte ) 0xB0 : 1018 case ( byte ) 0xB1 : 1019 case ( byte ) 0xB2 : 1020 case ( byte ) 0xB3 : 1021 case ( byte ) 0xB4 : 1022 case ( byte ) 0xB5 : 1023 case ( byte ) 0xB6 : 1024 case ( byte ) 0xB7 : 1025 case ( byte ) 0xB8 : 1026 case ( byte ) 0xB9 : 1027 case ( byte ) 0xBA : 1028 case ( byte ) 0xBB : 1029 case ( byte ) 0xBC : 1030 case ( byte ) 0xBD : 1031 case ( byte ) 0xBE : 1032 case ( byte ) 0xBF : 1033 case ( byte ) 0xC0 : 1034 case ( byte ) 0xC1 : 1035 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1036 break; 1037 1038 // Between 0xC2 and 0xDF, we may have a UTF-2 char 1039 case ( byte ) 0xC2 : 1040 case ( byte ) 0xC3 : 1041 case ( byte ) 0xC4 : 1042 case ( byte ) 0xC5 : 1043 case ( byte ) 0xC6 : 1044 case ( byte ) 0xC7 : 1045 case ( byte ) 0xC8 : 1046 case ( byte ) 0xC9 : 1047 case ( byte ) 0xCA : 1048 case ( byte ) 0xCB : 1049 case ( byte ) 0xCC : 1050 case ( byte ) 0xCD : 1051 case ( byte ) 0xCE : 1052 case ( byte ) 0xCF : 1053 case ( byte ) 0xD0 : 1054 case ( byte ) 0xD1 : 1055 case ( byte ) 0xD2 : 1056 case ( byte ) 0xD3 : 1057 case ( byte ) 0xD4 : 1058 case ( byte ) 0xD5 : 1059 case ( byte ) 0xD6 : 1060 case ( byte ) 0xD7 : 1061 case ( byte ) 0xD8 : 1062 case ( byte ) 0xD9 : 1063 case ( byte ) 0xDA : 1064 case ( byte ) 0xDB : 1065 case ( byte ) 0xDC : 1066 case ( byte ) 0xDD : 1067 case ( byte ) 0xDE : 1068 case ( byte ) 0xDF : 1069 // UTF2, if the following byte is in [0x80-0xBF] 1070 if ( trailChar ) 1071 { 1072 // No next byte : this is an octet 1073 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1074 } 1075 else 1076 { 1077 int b2 = bytes[pos + 1] & 0x00FF; 1078 1079 if ( ( b2 >= 0x0080 ) && ( b2 <= 0x00BF ) ) 1080 { 1081 // This is an UTF-2 char 1082 sb.append( Strings.utf8ToString( bytes, pos, 2 ) ); 1083 pos++; 1084 } 1085 else 1086 { 1087 // Not an UTF-2 1088 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1089 } 1090 } 1091 1092 break; 1093 1094 case ( byte ) 0xE0 : 1095 // May be an UTF-3, if the next byte is in [0xA0-0xBF], followed by a byte in [0x80-0xBF] 1096 if ( trailChar ) 1097 { 1098 // No next byte : this is an octet 1099 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1100 break; 1101 } 1102 1103 if ( pos == bytes.length - 2 ) 1104 { 1105 // We only have 2 bytes : not an UTF-3 1106 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1107 } 1108 else 1109 { 1110 int b2 = bytes[pos + 1] & 0x00FF; 1111 1112 if ( ( b2 >= 0x00A0 ) && ( b2 <= 0x00BF ) ) 1113 { 1114 int b3 = bytes[pos + 2] & 0x00FF; 1115 1116 // Check that the third byte is in between 0x80-0xBF 1117 if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) ) 1118 { 1119 // UTF-3 1120 sb.append( Strings.utf8ToString( bytes, pos, 3 ) ); 1121 pos += 2; 1122 } 1123 else 1124 { 1125 // Not an UTF-3, dump one bytes 1126 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1127 } 1128 } 1129 else 1130 { 1131 // Not an UTF-3 : dump two byte 1132 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1133 } 1134 } 1135 1136 break; 1137 1138 1139 // Between E1 and EC, this may be an UTF-3 if the next two bytes are between 0x80 and 0xBF 1140 case ( byte ) 0xE1 : 1141 case ( byte ) 0xE2 : 1142 case ( byte ) 0xE3 : 1143 case ( byte ) 0xE4 : 1144 case ( byte ) 0xE5 : 1145 case ( byte ) 0xE6 : 1146 case ( byte ) 0xE7 : 1147 case ( byte ) 0xE8 : 1148 case ( byte ) 0xE9 : 1149 case ( byte ) 0xEA : 1150 case ( byte ) 0xEB : 1151 case ( byte ) 0xEC : 1152 case ( byte ) 0xEE : 1153 case ( byte ) 0xEF : 1154 if ( trailChar ) 1155 { 1156 // No next byte : this is an octet 1157 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1158 break; 1159 } 1160 1161 if ( pos == bytes.length - 2 ) 1162 { 1163 // We only have 2 bytes : not an UTF-3 1164 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1165 } 1166 else 1167 { 1168 int b2 = bytes[pos + 1] & 0x00FF; 1169 1170 if ( ( b2 >= 0x0080 ) && ( b2 <= 0x00BF ) ) 1171 { 1172 int b3 = bytes[pos + 2] & 0x00FF; 1173 1174 // Check that the third byte is in between 0x80-0xBF 1175 if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) ) 1176 { 1177 // UTF-3 1178 sb.append( Strings.utf8ToString( bytes, pos, 3 ) ); 1179 pos += 2; 1180 } 1181 else 1182 { 1183 // Not an UTF-3, dump one byte 1184 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1185 } 1186 } 1187 else 1188 { 1189 // Not an UTF-3 : dump one byte 1190 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1191 pos++; 1192 } 1193 } 1194 1195 break; 1196 1197 case ( byte ) 0xED : 1198 // May be an UTF-3 if the second byte is in [0x80-0x9F] and the third byte in [0x80-0xBF] 1199 if ( trailChar ) 1200 { 1201 // No next byte : this is an octet 1202 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1203 break; 1204 } 1205 1206 if ( pos == bytes.length - 2 ) 1207 { 1208 // We only have 2 bytes : not an UTF-3 1209 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1210 } 1211 else 1212 { 1213 int b2 = bytes[pos + 1] & 0x00FF; 1214 1215 if ( ( b2 >= 0x0080 ) && ( b2 <= 0x009F ) ) 1216 { 1217 int b3 = bytes[pos + 2] & 0x00FF; 1218 1219 // Check that the third byte is in between 0x80-0xBF 1220 if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) ) 1221 { 1222 // UTF-3 1223 sb.append( Strings.utf8ToString( bytes, pos, 3 ) ); 1224 pos += 2; 1225 } 1226 else 1227 { 1228 // Not an UTF-3, dump one byte 1229 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1230 } 1231 } 1232 else 1233 { 1234 // Not an UTF-3 : dump one byte 1235 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1236 pos++; 1237 } 1238 } 1239 1240 break; 1241 1242 case ( byte ) 0xF0 : 1243 // May be an UTF-4 if the second byte is in [0x90-0xBF] followed by two bytes in [0x80-0xBF] 1244 if ( trailChar ) 1245 { 1246 // No next byte : this is an octet 1247 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1248 break; 1249 } 1250 1251 if ( pos == bytes.length - 3 ) 1252 { 1253 // We only have 2 bytes : not an UTF-4 1254 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1255 } 1256 else 1257 { 1258 int b2 = bytes[pos + 1] & 0x00FF; 1259 1260 if ( ( b2 >= 0x0090 ) && ( b2 <= 0x00BF ) ) 1261 { 1262 int b3 = bytes[pos + 2] & 0x00FF; 1263 1264 // Check that the third byte is in between 0x80-0xBF 1265 if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) ) 1266 { 1267 int b4 = bytes[pos + 3] & 0x00FF; 1268 1269 // Check that the forth byte is in between 0x80-0xBF 1270 if ( ( b4 >= 0x0080 ) && ( b4 <= 0x00BF ) ) 1271 { 1272 // UTF-4 1273 sb.append( Strings.utf8ToString( bytes, pos, 4 ) ); 1274 pos += 3; 1275 } 1276 else 1277 { 1278 // Not an UTF-4, dump one byte 1279 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1280 } 1281 } 1282 else 1283 { 1284 // Not an UTF-4, dump one byte 1285 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1286 } 1287 } 1288 else 1289 { 1290 // Not an UTF-4 : dump one byte 1291 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1292 pos++; 1293 } 1294 } 1295 1296 break; 1297 1298 case ( byte ) 0xF1 : 1299 case ( byte ) 0xF2 : 1300 case ( byte ) 0xF3 : 1301 // May be an UTF-4 1302 // May be an UTF-4 if it's followed by three bytes in [0x80-0xBF] 1303 if ( trailChar ) 1304 { 1305 // No next byte : this is an octet 1306 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1307 break; 1308 } 1309 1310 if ( pos == bytes.length - 3 ) 1311 { 1312 // We only have 2 bytes : not an UTF-4 1313 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1314 } 1315 else 1316 { 1317 int b2 = bytes[pos + 1] & 0x00FF; 1318 1319 if ( ( b2 >= 0x0080 ) && ( b2 <= 0x00BF ) ) 1320 { 1321 int b3 = bytes[pos + 2] & 0x00FF; 1322 1323 // Check that the third byte is in between 0x80-0xBF 1324 if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) ) 1325 { 1326 int b4 = bytes[pos + 3] & 0x00FF; 1327 1328 // Check that the forth byte is in between 0x80-0xBF 1329 if ( ( b4 >= 0x0080 ) && ( b4 <= 0x00BF ) ) 1330 { 1331 // UTF-4 1332 sb.append( Strings.utf8ToString( bytes, pos, 4 ) ); 1333 pos += 3; 1334 } 1335 else 1336 { 1337 // Not an UTF-4, dump one byte 1338 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1339 } 1340 } 1341 else 1342 { 1343 // Not an UTF-4, dump one byte 1344 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1345 } 1346 } 1347 else 1348 { 1349 // Not an UTF-4 : dump one byte 1350 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1351 pos++; 1352 } 1353 } 1354 1355 break; 1356 1357 case ( byte ) 0xF4 : 1358 // May be an UTF-4 if the second byte is in [0x80-0x8F] followed by two bytes in [0x80-0xBF] 1359 if ( trailChar ) 1360 { 1361 // No next byte : this is an octet 1362 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1363 break; 1364 } 1365 1366 if ( pos == bytes.length - 3 ) 1367 { 1368 // We only have 2 bytes : not an UTF-4 1369 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1370 } 1371 else 1372 { 1373 int b2 = bytes[pos + 1] & 0x00FF; 1374 1375 if ( ( b2 >= 0x0080 ) && ( b2 <= 0x008F ) ) 1376 { 1377 int b3 = bytes[pos + 2] & 0x00FF; 1378 1379 // Check that the third byte is in between 0x80-0xBF 1380 if ( ( b3 >= 0x0080 ) && ( b3 <= 0x00BF ) ) 1381 { 1382 int b4 = bytes[pos + 3] & 0x00FF; 1383 1384 // Check that the forth byte is in between 0x80-0xBF 1385 if ( ( b4 >= 0x0080 ) && ( b4 <= 0x00BF ) ) 1386 { 1387 // UTF-4 1388 sb.append( Strings.utf8ToString( bytes, pos, 4 ) ); 1389 pos += 3; 1390 } 1391 else 1392 { 1393 // Not an UTF-4, dump one byte 1394 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1395 } 1396 } 1397 else 1398 { 1399 // Not an UTF-4, dump one byte 1400 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1401 } 1402 } 1403 else 1404 { 1405 // Not an UTF-4 : dump one byte 1406 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1407 pos++; 1408 } 1409 } 1410 1411 break; 1412 1413 1414 default : 1415 // octet 1416 sb.append( '\\' ).append( Strings.byteToString( b ) ); 1417 1418 break; 1419 1420 } 1421 1422 if ( leadChar ) 1423 { 1424 leadChar = false; 1425 } 1426 } 1427 1428 return sb.toString(); 1429 } 1430 1431 1432 /** 1433 * Implements the cloning. 1434 * 1435 * @return a clone of this object 1436 */ 1437 @Override 1438 public Ava clone() 1439 { 1440 try 1441 { 1442 Ava clone = ( Ava ) super.clone(); 1443 clone.value = value.clone(); 1444 1445 return clone; 1446 } 1447 catch ( CloneNotSupportedException cnse ) 1448 { 1449 throw new Error( I18n.err( I18n.ERR_13621_ASSERTION_FAILURE ), cnse ); 1450 } 1451 } 1452 1453 1454 /** 1455 * Gets the hashcode of this object. 1456 * 1457 * @see java.lang.Object#hashCode() 1458 * @return The instance hash code 1459 */ 1460 @Override 1461 public int hashCode() 1462 { 1463 if ( h == 0 ) 1464 { 1465 int hTmp = 37; 1466 1467 hTmp = hTmp * 17 + ( normType != null ? normType.hashCode() : 0 ); 1468 h = hTmp * 17 + ( value != null ? value.hashCode() : 0 ); 1469 } 1470 1471 return h; 1472 } 1473 1474 1475 /** 1476 * @see Object#equals(Object) 1477 */ 1478 @Override 1479 public boolean equals( Object obj ) 1480 { 1481 if ( this == obj ) 1482 { 1483 return true; 1484 } 1485 1486 if ( !( obj instanceof Ava ) ) 1487 { 1488 return false; 1489 } 1490 1491 Ava instance = ( Ava ) obj; 1492 1493 // Compare the type 1494 if ( attributeType == null ) 1495 { 1496 if ( normType == null ) 1497 { 1498 if ( instance.normType != null ) 1499 { 1500 return false; 1501 } 1502 } 1503 else 1504 { 1505 if ( !normType.equals( instance.normType ) ) 1506 { 1507 return false; 1508 } 1509 } 1510 } 1511 else 1512 { 1513 if ( instance.getAttributeType() == null ) 1514 { 1515 if ( ( schemaManager != null ) 1516 && !attributeType.equals( schemaManager.getAttributeType( instance.getType() ) ) ) 1517 { 1518 return false; 1519 } 1520 } 1521 else if ( !attributeType.equals( instance.getAttributeType() ) ) 1522 { 1523 return false; 1524 } 1525 } 1526 1527 // Compare the values 1528 if ( ( value == null ) || value.isNull() ) 1529 { 1530 return ( instance.value == null ) || instance.value.isNull(); 1531 } 1532 else 1533 { 1534 if ( schemaManager != null ) 1535 { 1536 if ( ( value.getString() != null ) && value.getString().equals( instance.value.getString() ) ) 1537 { 1538 return true; 1539 } 1540 1541 if ( attributeType == null ) 1542 { 1543 attributeType = schemaManager.getAttributeType( normType ); 1544 } 1545 1546 MatchingRule equalityMatchingRule = attributeType.getEquality(); 1547 1548 if ( equalityMatchingRule != null ) 1549 { 1550 Normalizer normalizer = equalityMatchingRule.getNormalizer(); 1551 1552 try 1553 { 1554 return equalityMatchingRule.getLdapComparator().compare( normalizer.normalize( value.getString() ), 1555 instance.value.getString() ) == 0; 1556 } 1557 catch ( LdapException le ) 1558 { 1559 // TODO: is this OK? If the comparison is not reliable without normalization then we should throw exception 1560 // instead returning false. Returning false may be misleading and the log message can be easily overlooked. 1561 // If the comparison is reliable, this should not really be an error. Maybe use debug or trace instead? 1562 LOG.error( I18n.err( I18n.ERR_13620_CANNOT_NORMALIZE_VALUE ), le.getMessage() ); 1563 return false; 1564 } 1565 } 1566 else 1567 { 1568 // No Equality MR, use a direct comparison 1569 if ( !value.isHumanReadable() ) 1570 { 1571 return Arrays.equals( value.getBytes(), instance.value.getBytes() ); 1572 } 1573 else 1574 { 1575 return value.getString().equals( instance.value.getString() ); 1576 } 1577 } 1578 } 1579 else 1580 { 1581 return value.equals( instance.value ); 1582 } 1583 } 1584 } 1585 1586 1587 /** 1588 * Serialize the AVA into a buffer at the given position. 1589 * 1590 * @param buffer The buffer which will contain the serialized Ava 1591 * @param pos The position in the buffer for the serialized value 1592 * @return The new position in the buffer 1593 * @throws IOException Id the serialization failed 1594 */ 1595 public int serialize( byte[] buffer, int pos ) throws IOException 1596 { 1597 if ( Strings.isEmpty( upName ) 1598 || Strings.isEmpty( upType ) 1599 || Strings.isEmpty( normType ) 1600 || ( value.isNull() ) ) 1601 { 1602 String message; 1603 1604 if ( Strings.isEmpty( upName ) ) 1605 { 1606 message = I18n.err( I18n.ERR_13616_CANNOT_SERIALIZE_AVA_UPNAME_NULL ); 1607 } 1608 else if ( Strings.isEmpty( upType ) ) 1609 { 1610 message = I18n.err( I18n.ERR_13617_CANNOT_SERIALIZE_AVA_UPTYPE_NULL ); 1611 } 1612 else if ( Strings.isEmpty( normType ) ) 1613 { 1614 message = I18n.err( I18n.ERR_13618_CANNOT_SERIALIZE_AVA_NORMTYPE_NULL ); 1615 } 1616 else 1617 { 1618 message = I18n.err( I18n.ERR_13619_CANNOT_SERIALIZE_AVA_VALUE_NULL ); 1619 } 1620 1621 LOG.error( message ); 1622 throw new IOException( message ); 1623 } 1624 1625 int length = 0; 1626 1627 // The upName 1628 byte[] upNameBytes = null; 1629 1630 if ( upName != null ) 1631 { 1632 upNameBytes = Strings.getBytesUtf8( upName ); 1633 length += 1 + 4 + upNameBytes.length; 1634 } 1635 1636 // The upType 1637 byte[] upTypeBytes = null; 1638 1639 if ( upType != null ) 1640 { 1641 upTypeBytes = Strings.getBytesUtf8( upType ); 1642 length += 1 + 4 + upTypeBytes.length; 1643 } 1644 1645 // Is HR 1646 length++; 1647 1648 // The hash code 1649 length += 4; 1650 1651 // Check that we will be able to store the data in the buffer 1652 if ( buffer.length - pos < length ) 1653 { 1654 throw new ArrayIndexOutOfBoundsException(); 1655 } 1656 1657 // Write the upName 1658 if ( upName != null ) 1659 { 1660 buffer[pos++] = Serialize.TRUE; 1661 pos = Serialize.serialize( upNameBytes, buffer, pos ); 1662 } 1663 else 1664 { 1665 buffer[pos++] = Serialize.FALSE; 1666 } 1667 1668 // Write the upType 1669 if ( upType != null ) 1670 { 1671 buffer[pos++] = Serialize.TRUE; 1672 pos = Serialize.serialize( upTypeBytes, buffer, pos ); 1673 } 1674 else 1675 { 1676 buffer[pos++] = Serialize.FALSE; 1677 } 1678 1679 // Write the isHR flag 1680 if ( value.isHumanReadable() ) 1681 { 1682 buffer[pos++] = Serialize.TRUE; 1683 } 1684 else 1685 { 1686 buffer[pos++] = Serialize.FALSE; 1687 } 1688 1689 // Write the upValue 1690 if ( value.isHumanReadable() ) 1691 { 1692 pos = value.serialize( buffer, pos ); 1693 } 1694 1695 // Write the hash code 1696 pos = Serialize.serialize( h, buffer, pos ); 1697 1698 return pos; 1699 } 1700 1701 1702 /** 1703 * Deserialize an AVA from a byte[], starting at a given position 1704 * 1705 * @param buffer The buffer containing the AVA 1706 * @param pos The position in the buffer 1707 * @return The new position 1708 * @throws IOException If the serialized value is not an AVA 1709 * @throws LdapInvalidAttributeValueException If the serialized AVA is invalid 1710 */ 1711 public int deserialize( byte[] buffer, int pos ) throws IOException, LdapInvalidAttributeValueException 1712 { 1713 if ( ( pos < 0 ) || ( pos >= buffer.length ) ) 1714 { 1715 throw new ArrayIndexOutOfBoundsException(); 1716 } 1717 1718 // Read the upName value, if it's not null 1719 boolean hasUpName = Serialize.deserializeBoolean( buffer, pos ); 1720 pos++; 1721 1722 if ( hasUpName ) 1723 { 1724 byte[] wrappedValueBytes = Serialize.deserializeBytes( buffer, pos ); 1725 pos += 4 + wrappedValueBytes.length; 1726 upName = Strings.utf8ToString( wrappedValueBytes ); 1727 } 1728 1729 // Read the upType value, if it's not null 1730 boolean hasUpType = Serialize.deserializeBoolean( buffer, pos ); 1731 pos++; 1732 1733 if ( hasUpType ) 1734 { 1735 byte[] upTypeBytes = Serialize.deserializeBytes( buffer, pos ); 1736 pos += 4 + upTypeBytes.length; 1737 upType = Strings.utf8ToString( upTypeBytes ); 1738 } 1739 1740 // Update the AtributeType 1741 if ( schemaManager != null ) 1742 { 1743 if ( !Strings.isEmpty( upType ) ) 1744 { 1745 attributeType = schemaManager.getAttributeType( upType ); 1746 } 1747 else 1748 { 1749 attributeType = schemaManager.getAttributeType( normType ); 1750 } 1751 } 1752 1753 if ( attributeType != null ) 1754 { 1755 normType = attributeType.getOid(); 1756 } 1757 else 1758 { 1759 normType = upType; 1760 } 1761 1762 // Read the isHR flag 1763 boolean isHR = Serialize.deserializeBoolean( buffer, pos ); 1764 pos++; 1765 1766 if ( isHR ) 1767 { 1768 // Read the upValue 1769 value = Value.createValue( attributeType ); 1770 pos = value.deserialize( buffer, pos ); 1771 } 1772 1773 // Read the hashCode 1774 h = Serialize.deserializeInt( buffer, pos ); 1775 pos += 4; 1776 1777 return pos; 1778 } 1779 1780 1781 /** 1782 * 1783 * An Ava is composed of a type and a value. 1784 * The data are stored following the structure : 1785 * <ul> 1786 * <li> 1787 * <b>upName</b> The User provided ATAV 1788 * </li> 1789 * <li> 1790 * <b>start</b> The position of this ATAV in the Dn 1791 * </li> 1792 * <li> 1793 * <b>length</b> The ATAV length 1794 * </li> 1795 * <li> 1796 * <b>upType</b> The user Provided Type 1797 * </li> 1798 * <li> 1799 * <b>normType</b> The normalized AttributeType 1800 * </li> 1801 * <li> 1802 * <b>isHR</b> Tells if the value is a String or not 1803 * </li> 1804 * </ul> 1805 * <br> 1806 * if the value is a String : 1807 * <ul> 1808 * <li> 1809 * <b>value</b> The value 1810 * </li> 1811 * </ul> 1812 * <br> 1813 * if the value is binary : 1814 * <ul> 1815 * <li> 1816 * <b>valueLength</b> 1817 * </li> 1818 * <li> 1819 * <b>value</b> The value 1820 * </li> 1821 * </ul> 1822 * 1823 * @see Externalizable#readExternal(ObjectInput) 1824 * 1825 * @throws IOException If the Ava can't be written in the stream 1826 */ 1827 @Override 1828 public void writeExternal( ObjectOutput out ) throws IOException 1829 { 1830 if ( Strings.isEmpty( upName ) 1831 || Strings.isEmpty( upType ) 1832 || Strings.isEmpty( normType ) 1833 || ( value.isNull() ) ) 1834 { 1835 String message; 1836 1837 if ( Strings.isEmpty( upName ) ) 1838 { 1839 message = I18n.err( I18n.ERR_13616_CANNOT_SERIALIZE_AVA_UPNAME_NULL ); 1840 } 1841 else if ( Strings.isEmpty( upType ) ) 1842 { 1843 message = I18n.err( I18n.ERR_13617_CANNOT_SERIALIZE_AVA_UPTYPE_NULL ); 1844 } 1845 else if ( Strings.isEmpty( normType ) ) 1846 { 1847 message = I18n.err( I18n.ERR_13618_CANNOT_SERIALIZE_AVA_NORMTYPE_NULL ); 1848 } 1849 else 1850 { 1851 message = I18n.err( I18n.ERR_13619_CANNOT_SERIALIZE_AVA_VALUE_NULL ); 1852 } 1853 1854 LOG.error( message ); 1855 throw new IOException( message ); 1856 } 1857 1858 if ( upName != null ) 1859 { 1860 out.writeBoolean( true ); 1861 out.writeUTF( upName ); 1862 } 1863 else 1864 { 1865 out.writeBoolean( false ); 1866 } 1867 1868 if ( upType != null ) 1869 { 1870 out.writeBoolean( true ); 1871 out.writeUTF( upType ); 1872 } 1873 else 1874 { 1875 out.writeBoolean( false ); 1876 } 1877 1878 if ( normType != null ) 1879 { 1880 out.writeBoolean( true ); 1881 out.writeUTF( normType ); 1882 } 1883 else 1884 { 1885 out.writeBoolean( false ); 1886 } 1887 1888 boolean isHR = value.isHumanReadable(); 1889 1890 out.writeBoolean( isHR ); 1891 1892 value.writeExternal( out ); 1893 1894 // Write the hashCode 1895 out.writeInt( h ); 1896 1897 out.flush(); 1898 } 1899 1900 1901 /** 1902 * We read back the data to create a new ATAV. The structure 1903 * read is exposed in the {@link Ava#writeExternal(ObjectOutput)} 1904 * method 1905 * 1906 * @see Externalizable#readExternal(ObjectInput) 1907 * 1908 * @throws IOException If the Ava can't b written to the stream 1909 * @throws ClassNotFoundException If we can't deserialize an Ava from the stream 1910 */ 1911 @Override 1912 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException 1913 { 1914 boolean hasUpName = in.readBoolean(); 1915 1916 if ( hasUpName ) 1917 { 1918 upName = in.readUTF(); 1919 } 1920 1921 boolean hasUpType = in.readBoolean(); 1922 1923 if ( hasUpType ) 1924 { 1925 upType = in.readUTF(); 1926 } 1927 1928 boolean hasNormType = in.readBoolean(); 1929 1930 if ( hasNormType ) 1931 { 1932 normType = in.readUTF(); 1933 } 1934 1935 if ( schemaManager != null ) 1936 { 1937 if ( !Strings.isEmpty( upType ) ) 1938 { 1939 attributeType = schemaManager.getAttributeType( upType ); 1940 } 1941 else 1942 { 1943 attributeType = schemaManager.getAttributeType( normType ); 1944 } 1945 } 1946 1947 in.readBoolean(); 1948 1949 value = Value.deserialize( attributeType, in ); 1950 1951 h = in.readInt(); 1952 } 1953 1954 1955 /** 1956 * Tells if the Ava is schema aware or not. 1957 * 1958 * @return <tt>true</tt> if the Ava is schema aware 1959 */ 1960 public boolean isSchemaAware() 1961 { 1962 return attributeType != null; 1963 } 1964 1965 1966 /** 1967 * @return the attributeType 1968 */ 1969 public AttributeType getAttributeType() 1970 { 1971 return attributeType; 1972 } 1973 1974 1975 private int compareValues( Ava that ) 1976 { 1977 int comp; 1978 1979 if ( value.isHumanReadable() ) 1980 { 1981 comp = value.compareTo( that.value ); 1982 1983 return comp; 1984 } 1985 else 1986 { 1987 byte[] bytes1 = value.getBytes(); 1988 byte[] bytes2 = that.value.getBytes(); 1989 1990 for ( int pos = 0; pos < bytes1.length; pos++ ) 1991 { 1992 int v1 = bytes1[pos] & 0x00FF; 1993 int v2 = bytes2[pos] & 0x00FF; 1994 1995 if ( v1 > v2 ) 1996 { 1997 return 1; 1998 } 1999 else if ( v2 > v1 ) 2000 { 2001 return -1; 2002 } 2003 } 2004 2005 return 0; 2006 } 2007 2008 } 2009 2010 2011 /** 2012 * @see Comparable#compareTo(Object) 2013 */ 2014 @Override 2015 public int compareTo( Ava that ) 2016 { 2017 if ( that == null ) 2018 { 2019 return 1; 2020 } 2021 2022 int comp; 2023 2024 if ( schemaManager == null ) 2025 { 2026 // Compare the ATs 2027 comp = normType.compareTo( that.normType ); 2028 2029 if ( comp != 0 ) 2030 { 2031 return comp; 2032 } 2033 2034 // and compare the values 2035 if ( value == null ) 2036 { 2037 if ( that.value == null ) 2038 { 2039 return 0; 2040 } 2041 else 2042 { 2043 return -1; 2044 } 2045 } 2046 else 2047 { 2048 if ( that.value == null ) 2049 { 2050 return 1; 2051 } 2052 else 2053 { 2054 comp = value.compareTo( ( Value ) that.value ); 2055 2056 return comp; 2057 } 2058 } 2059 } 2060 else 2061 { 2062 if ( that.schemaManager == null ) 2063 { 2064 // Problem : we will apply the current Ava SchemaManager to the given Ava 2065 try 2066 { 2067 that.apply( schemaManager ); 2068 } 2069 catch ( LdapInvalidDnException lide ) 2070 { 2071 return 1; 2072 } 2073 } 2074 2075 // First compare the AT OID 2076 comp = attributeType.getOid().compareTo( that.attributeType.getOid() ); 2077 2078 if ( comp != 0 ) 2079 { 2080 return comp; 2081 } 2082 2083 // Now, compare the two values using the ordering matchingRule comparator, if any 2084 MatchingRule orderingMR = attributeType.getOrdering(); 2085 2086 if ( orderingMR != null ) 2087 { 2088 LdapComparator<Object> comparator = ( LdapComparator<Object> ) orderingMR.getLdapComparator(); 2089 2090 if ( comparator != null ) 2091 { 2092 comp = value.compareTo( that.value ); 2093 2094 return comp; 2095 } 2096 else 2097 { 2098 comp = compareValues( that ); 2099 2100 return comp; 2101 } 2102 } 2103 else 2104 { 2105 comp = compareValues( that ); 2106 2107 return comp; 2108 } 2109 } 2110 } 2111 2112 2113 /** 2114 * A String representation of an Ava, as provided by the user. 2115 * 2116 * @return A string representing an Ava 2117 */ 2118 @Override 2119 public String toString() 2120 { 2121 return upName; 2122 } 2123}