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.ArrayList; 028import java.util.Collection; 029import java.util.Iterator; 030import java.util.List; 031 032import org.apache.commons.collections.MultiMap; 033import org.apache.commons.collections.map.MultiValueMap; 034import org.apache.directory.api.i18n.I18n; 035import org.apache.directory.api.ldap.model.entry.StringValue; 036import org.apache.directory.api.ldap.model.entry.Value; 037import org.apache.directory.api.ldap.model.exception.LdapException; 038import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; 039import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException; 040import org.apache.directory.api.ldap.model.schema.AttributeType; 041import org.apache.directory.api.ldap.model.schema.SchemaManager; 042import org.apache.directory.api.ldap.model.schema.normalizers.OidNormalizer; 043import org.apache.directory.api.util.Chars; 044import org.apache.directory.api.util.Hex; 045import org.apache.directory.api.util.Serialize; 046import org.apache.directory.api.util.StringConstants; 047import org.apache.directory.api.util.Strings; 048import org.apache.directory.api.util.Unicode; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052 053/** 054 * This class store the name-component part or the following BNF grammar (as of 055 * RFC2253, par. 3, and RFC1779, fig. 1) : <br> - <name-component> ::= 056 * <attributeType> <spaces> '=' <spaces> 057 * <attributeValue> <attributeTypeAndValues> <br> - 058 * <attributeTypeAndValues> ::= <spaces> '+' <spaces> 059 * <attributeType> <spaces> '=' <spaces> 060 * <attributeValue> <attributeTypeAndValues> | e <br> - 061 * <attributeType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] 062 * <digits> <oids> | [0-9] <digits> <oids> <br> - 063 * <keychars> ::= [a-zA-Z] <keychars> | [0-9] <keychars> | '-' 064 * <keychars> | e <br> - <oidPrefix> ::= 'OID.' | 'oid.' | e <br> - 065 * <oids> ::= '.' [0-9] <digits> <oids> | e <br> - 066 * <attributeValue> ::= <pairs-or-strings> | '#' <hexstring> 067 * |'"' <quotechar-or-pairs> '"' <br> - <pairs-or-strings> ::= '\' 068 * <pairchar> <pairs-or-strings> | <stringchar> 069 * <pairs-or-strings> | e <br> - <quotechar-or-pairs> ::= 070 * <quotechar> <quotechar-or-pairs> | '\' <pairchar> 071 * <quotechar-or-pairs> | e <br> - <pairchar> ::= ',' | '=' | '+' | 072 * '<' | '>' | '#' | ';' | '\' | '"' | [0-9a-fA-F] [0-9a-fA-F] <br> - 073 * <hexstring> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> <br> - 074 * <hexpairs> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> | e <br> - 075 * <digits> ::= [0-9] <digits> | e <br> - <stringchar> ::= 076 * [0x00-0xFF] - [,=+<>#;\"\n\r] <br> - <quotechar> ::= [0x00-0xFF] - 077 * [\"] <br> - <separator> ::= ',' | ';' <br> - <spaces> ::= ' ' 078 * <spaces> | e <br> 079 * <br> 080 * A Rdn is a part of a Dn. It can be composed of many types, as in the Rdn 081 * following Rdn :<br> 082 * ou=value + cn=other value<br> 083 * <br> 084 * or <br> 085 * ou=value + ou=another value<br> 086 * <br> 087 * In this case, we have to store an 'ou' and a 'cn' in the Rdn.<br> 088 * <br> 089 * The types are case insensitive. <br> 090 * Spaces before and after types and values are not stored.<br> 091 * Spaces before and after '+' are not stored.<br> 092 * <br> 093 * Thus, we can consider that the following RDNs are equals :<br> 094 * <br> 095 * 'ou=test 1'<br> ' ou=test 1'<br> 096 * 'ou =test 1'<br> 097 * 'ou= test 1'<br> 098 * 'ou=test 1 '<br> ' ou = test 1 '<br> 099 * <br> 100 * So are the following :<br> 101 * <br> 102 * 'ou=test 1+cn=test 2'<br> 103 * 'ou = test 1 + cn = test 2'<br> ' ou =test 1+ cn =test 2 ' <br> 104 * 'cn = test 2 +ou = test 1'<br> 105 * <br> 106 * but the following are not equal :<br> 107 * 'ou=test 1' <br> 108 * 'ou=test 1'<br> 109 * because we have more than one spaces inside the value.<br> 110 * <br> 111 * The Rdn is composed of one or more Ava. Those Avas 112 * are ordered in the alphabetical natural order : a < b < c ... < z As the type 113 * are not case sensitive, we can say that a = A 114 * <br> 115 * This class is immutable. 116 * 117 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 118 */ 119public class Rdn implements Cloneable, Externalizable, Iterable<Ava>, Comparable<Rdn> 120{ 121 /** The LoggerFactory used by this class */ 122 protected static final Logger LOG = LoggerFactory.getLogger( Rdn.class ); 123 124 /** An empty Rdn */ 125 public static final Rdn EMPTY_RDN = new Rdn(); 126 127 /** 128 * Declares the Serial Version Uid. 129 * 130 * @see <a 131 * href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always 132 * Declare Serial Version Uid</a> 133 */ 134 private static final long serialVersionUID = 1L; 135 136 /** The User Provided Rdn */ 137 private String upName = null; 138 139 /** The normalized Rdn */ 140 private String normName = null; 141 142 /** 143 * Stores all couple type = value. We may have more than one type, if the 144 * '+' character appears in the Ava. This is a TreeSet, 145 * because we want the Avas to be sorted. An Ava may contain more than one 146 * value. In this case, the values are String stored in a List. 147 */ 148 private List<Ava> avas = null; 149 150 /** 151 * We also keep a set of types, in order to use manipulations. A type is 152 * connected with the Ava it represents. 153 * 154 * Note : there is no Generic available classes in commons-collection... 155 */ 156 private MultiMap avaTypes = new MultiValueMap(); 157 158 /** 159 * We keep the type for a single valued Rdn, to avoid the creation of an HashMap 160 */ 161 private String avaType = null; 162 163 /** 164 * A simple Ava is used to store the Rdn for the simple 165 * case where we only have a single type=value. This will be 99.99% the 166 * case. This avoids the creation of a HashMap. 167 */ 168 protected Ava ava = null; 169 170 /** 171 * The number of Avas. We store this number here to avoid complex 172 * manipulation of Ava and Avas 173 */ 174 private int nbAvas = 0; 175 176 /** CompareTo() results */ 177 public static final int UNDEFINED = Integer.MAX_VALUE; 178 179 /** Constant used in comparisons */ 180 public static final int SUPERIOR = 1; 181 182 /** Constant used in comparisons */ 183 public static final int INFERIOR = -1; 184 185 /** Constant used in comparisons */ 186 public static final int EQUAL = 0; 187 188 /** A flag used to tell if the Rdn has been normalized */ 189 private boolean normalized = false; 190 191 /** the schema manager */ 192 private SchemaManager schemaManager; 193 194 /** The computed hashcode */ 195 private volatile int h; 196 197 198 /** 199 * A empty constructor. 200 */ 201 public Rdn() 202 { 203 this( ( SchemaManager ) null ); 204 } 205 206 207 /** 208 * 209 * Creates a new schema aware instance of Rdn. 210 * 211 * @param schemaManager the schema manager 212 */ 213 public Rdn( SchemaManager schemaManager ) 214 { 215 // Don't waste space... This is not so often we have multiple 216 // name-components in a Rdn... So we won't initialize the Map and the 217 // treeSet. 218 this.schemaManager = schemaManager; 219 upName = ""; 220 normName = ""; 221 normalized = false; 222 h = 0; 223 } 224 225 226 /** 227 * A constructor that parse a String representing a schema aware Rdn. 228 * 229 * @param schemaManager the schema manager 230 * @param rdn the String containing the Rdn to parse 231 * @throws LdapInvalidDnException if the Rdn is invalid 232 */ 233 public Rdn( SchemaManager schemaManager, String rdn ) throws LdapInvalidDnException 234 { 235 if ( Strings.isNotEmpty( rdn ) ) 236 { 237 // Parse the string. The Rdn will be updated. 238 parse( rdn, this ); 239 240 // create the internal normalized form 241 // and store the user provided form 242 if ( schemaManager != null ) 243 { 244 this.schemaManager = schemaManager; 245 apply( schemaManager ); 246 normalized = true; 247 } 248 else 249 { 250 normalize(); 251 normalized = false; 252 } 253 254 if ( upName.length() < rdn.length() ) 255 { 256 throw new LdapInvalidDnException( "Invalid RDN" ); 257 } 258 259 upName = rdn; 260 } 261 else 262 { 263 upName = ""; 264 normName = ""; 265 normalized = false; 266 } 267 268 hashCode(); 269 } 270 271 272 /** 273 * A constructor that parse a String representing a Rdn. 274 * 275 * @param rdn the String containing the Rdn to parse 276 * @throws LdapInvalidDnException if the Rdn is invalid 277 */ 278 public Rdn( String rdn ) throws LdapInvalidDnException 279 { 280 this( ( SchemaManager ) null, rdn ); 281 } 282 283 284 /** 285 * A constructor that constructs a schema aware Rdn from a type and a value. 286 * <p> 287 * The string attribute values are not interpreted as RFC 414 formatted Rdn 288 * strings. That is, the values are used literally (not parsed) and assumed 289 * to be un-escaped. 290 * 291 * @param schemaManager the schema manager 292 * @param upType the user provided type of the Rdn 293 * @param upValue the user provided value of the Rdn 294 * @throws LdapInvalidDnException if the Rdn is invalid 295 */ 296 public Rdn( SchemaManager schemaManager, String upType, String upValue ) throws LdapInvalidDnException 297 { 298 addAVA( schemaManager, upType, upType, new StringValue( upValue ), new StringValue( upValue ) ); 299 300 upName = upType + '=' + upValue; 301 302 if ( schemaManager != null ) 303 { 304 this.schemaManager = schemaManager; 305 apply( schemaManager ); 306 normalized = true; 307 } 308 else 309 { 310 // create the internal normalized form 311 normalize(); 312 313 // As strange as it seems, the Rdn is *not* normalized against the schema at this point 314 normalized = false; 315 } 316 317 hashCode(); 318 } 319 320 321 /** 322 * A constructor that constructs a Rdn from a type and a value. 323 * 324 * @param upType the user provided type of the Rdn 325 * @param upValue the user provided value of the Rdn 326 * @throws LdapInvalidDnException if the Rdn is invalid 327 * @see #Rdn( SchemaManager, String, String ) 328 */ 329 public Rdn( String upType, String upValue ) throws LdapInvalidDnException 330 { 331 this( null, upType, upValue ); 332 } 333 334 335 public Rdn( SchemaManager schemaManager, Ava... avas ) throws LdapInvalidDnException 336 { 337 StringBuilder upName = new StringBuilder(); 338 for ( int i = 0; i < avas.length; i++ ) 339 { 340 if ( i > 0 ) 341 { 342 upName.append( '+' ); 343 } 344 addAVA( schemaManager, avas[i] ); 345 upName.append( avas[i].getName() ); 346 } 347 setUpName( upName.toString() ); 348 normalize(); 349 } 350 351 352 public Rdn( Ava... avas ) throws LdapInvalidDnException 353 { 354 this( null, avas ); 355 } 356 357 358 /** 359 * Constructs an Rdn from the given rdn. The content of the rdn is simply 360 * copied into the newly created Rdn. 361 * 362 * @param rdn The non-null Rdn to be copied. 363 */ 364 public Rdn( Rdn rdn ) 365 { 366 nbAvas = rdn.size(); 367 this.normName = rdn.normName; 368 this.upName = rdn.getName(); 369 normalized = rdn.normalized; 370 schemaManager = rdn.schemaManager; 371 372 switch ( rdn.size() ) 373 { 374 case 0: 375 hashCode(); 376 377 return; 378 379 case 1: 380 this.ava = rdn.ava.clone(); 381 hashCode(); 382 383 return; 384 385 default: 386 // We must duplicate the treeSet and the hashMap 387 avas = new ArrayList<Ava>(); 388 avaTypes = new MultiValueMap(); 389 390 for ( Ava currentAva : rdn.avas ) 391 { 392 avas.add( currentAva.clone() ); 393 avaTypes.put( currentAva.getNormType(), currentAva ); 394 } 395 396 hashCode(); 397 398 return; 399 } 400 } 401 402 403 /** 404 * Transform the external representation of the current Rdn to an internal 405 * normalized form where : 406 * - types are trimmed and lower cased 407 * - values are trimmed and lower cased 408 */ 409 // WARNING : The protection level is left unspecified on purpose. 410 // We need this method to be visible from the DnParser class, but not 411 // from outside this package. 412 /* Unspecified protection */void normalize() 413 { 414 switch ( nbAvas ) 415 { 416 case 0: 417 // An empty Rdn 418 normName = ""; 419 break; 420 421 case 1: 422 // We have a single Ava 423 // We will trim and lowercase type and value. 424 if ( ava.getNormValue().isHumanReadable() ) 425 { 426 normName = ava.getNormName(); 427 } 428 else 429 { 430 normName = ava.getNormType() + "=#" + Strings.dumpHexPairs( ava.getNormValue().getBytes() ); 431 } 432 433 break; 434 435 default: 436 // We have more than one Ava 437 StringBuffer sb = new StringBuffer(); 438 439 boolean isFirst = true; 440 441 for ( Ava ata : avas ) 442 { 443 if ( isFirst ) 444 { 445 isFirst = false; 446 } 447 else 448 { 449 sb.append( '+' ); 450 } 451 452 sb.append( ata.getNormName() ); 453 } 454 455 normName = sb.toString(); 456 break; 457 } 458 459 hashCode(); 460 } 461 462 463 /** 464 * Transform a Rdn by changing the value to its OID counterpart and 465 * normalizing the value accordingly to its type. The modified Rdn is 466 * a new instance, as the Rdn class is immutable. 467 * 468 * @param schemaManager the SchemaManager 469 * @return this Rdn, normalized 470 * @throws LdapInvalidDnException if the Rdn is invalid 471 */ 472 public Rdn apply( SchemaManager schemaManager ) throws LdapInvalidDnException 473 { 474 if ( normalized ) 475 { 476 return this; 477 } 478 479 String savedUpName = getName(); 480 Dn.rdnOidToName( this, schemaManager ); 481 normalize(); 482 this.upName = savedUpName; 483 normalized = true; 484 this.schemaManager = schemaManager; 485 hashCode(); 486 487 return this; 488 } 489 490 491 /** 492 * Add an Ava to the current Rdn 493 * 494 * @param upType The user provided type of the added Rdn. 495 * @param type The normalized provided type of the added Rdn. 496 * @param upValue The user provided value of the added Rdn 497 * @param value The normalized provided value of the added Rdn 498 * @throws LdapInvalidDnException 499 * If the Rdn is invalid 500 */ 501 private void addAVA( SchemaManager schemaManager, String upType, String type, Value<?> upValue, 502 Value<?> value ) throws LdapInvalidDnException 503 { 504 // First, let's normalize the type 505 Value<?> normalizedValue = value; 506 String normalizedType = Strings.lowerCaseAscii( type ); 507 this.schemaManager = schemaManager; 508 509 if ( schemaManager != null ) 510 { 511 OidNormalizer oidNormalizer = schemaManager.getNormalizerMapping().get( normalizedType ); 512 normalizedType = oidNormalizer.getAttributeTypeOid(); 513 514 try 515 { 516 normalizedValue = oidNormalizer.getNormalizer().normalize( value ); 517 } 518 catch ( LdapException e ) 519 { 520 throw new LdapInvalidDnException( e.getMessage(), e ); 521 } 522 } 523 524 switch ( nbAvas ) 525 { 526 case 0: 527 // This is the first Ava. Just stores it. 528 ava = new Ava( schemaManager, upType, normalizedType, upValue, normalizedValue ); 529 nbAvas = 1; 530 avaType = normalizedType; 531 hashCode(); 532 533 return; 534 535 case 1: 536 // We already have an Ava. We have to put it in the HashMap 537 // before adding a new one. 538 // First, create the HashMap, 539 avas = new ArrayList<Ava>(); 540 541 // and store the existing Ava into it. 542 avas.add( ava ); 543 avaTypes = new MultiValueMap(); 544 avaTypes.put( avaType, ava ); 545 546 ava = null; 547 548 // Now, fall down to the commmon case 549 // NO BREAK !!! 550 551 default: 552 // add a new Ava 553 Ava newAva = new Ava( schemaManager, upType, normalizedType, upValue, normalizedValue ); 554 avas.add( newAva ); 555 avaTypes.put( normalizedType, newAva ); 556 nbAvas++; 557 hashCode(); 558 559 return; 560 561 } 562 } 563 564 565 /** 566 * Replace an Ava into a Rdn at a given position 567 * 568 * @param value The modified Ava 569 * @param pos The position of the Ava in the Rdn 570 * @exception LdapInvalidDnException If the position is not valid 571 */ 572 // WARNING : The protection level is left unspecified intentionally. 573 // We need this method to be visible from the DnParser class, but not 574 // from outside this package. 575 /* Unspecified protection */void replaceAva( Ava value, int pos ) throws LdapInvalidDnException 576 { 577 if ( ( pos < 0 ) || ( pos > nbAvas ) ) 578 { 579 throw new LdapInvalidDnException( "Cannot set the AVA at position " + pos ); 580 } 581 582 String normalizedType = value.getNormType(); 583 584 switch ( nbAvas ) 585 { 586 case 1: 587 // This is the first Ava. Just stores it. 588 ava = value; 589 avaType = normalizedType; 590 591 break; 592 593 default: 594 Ava oldAva = avas.get( pos ); 595 avas.set( pos, value ); 596 avaTypes.remove( oldAva.getType() ); 597 avaTypes.put( normalizedType, value ); 598 599 break; 600 } 601 602 h = 0; 603 hashCode(); 604 } 605 606 607 /** 608 * Add an Ava to the current schema aware Rdn 609 * 610 * @param value The added Ava 611 */ 612 // WARNING : The protection level is left unspecified intentionally. 613 // We need this method to be visible from the DnParser class, but not 614 // from outside this package. 615 /* Unspecified protection */void addAVA( SchemaManager schemaManager, Ava value ) throws LdapInvalidDnException 616 { 617 this.schemaManager = schemaManager; 618 String normalizedType = value.getNormType(); 619 620 switch ( nbAvas ) 621 { 622 case 0: 623 // This is the first Ava. Just stores it. 624 ava = value; 625 nbAvas = 1; 626 avaType = normalizedType; 627 hashCode(); 628 629 return; 630 631 case 1: 632 // We already have an Ava. We have to put it in the HashMap 633 // before adding a new one. 634 // Check that the first AVA is not for the same attribute 635 if ( avaType.equals( normalizedType ) ) 636 { 637 throw new LdapInvalidDnException( "Invalid RDN: the " + normalizedType 638 + " is already present in the RDN" ); 639 } 640 641 // First, create the HashMap, 642 avas = new ArrayList<Ava>(); 643 644 // and store the existing Ava into it. 645 avas.add( ava ); 646 avaTypes = new MultiValueMap(); 647 avaTypes.put( avaType, ava ); 648 649 this.ava = null; 650 651 // Now, fall down to the commmon case 652 // NO BREAK !!! 653 654 default: 655 // Check that the AT is not already present 656 if ( avaTypes.containsKey( normalizedType ) ) 657 { 658 throw new LdapInvalidDnException( "Invalid RDN: the " + normalizedType 659 + " is already present in the RDN" ); 660 } 661 662 // add a new Ava 663 avas.add( value ); 664 avaTypes.put( normalizedType, value ); 665 nbAvas++; 666 hashCode(); 667 668 break; 669 } 670 } 671 672 673 /** 674 * Clear the Rdn, removing all the Avas. 675 */ 676 // WARNING : The protection level is left unspecified intentionally. 677 // We need this method to be visible from the DnParser class, but not 678 // from outside this package. 679 /* No protection */void clear() 680 { 681 ava = null; 682 avas = null; 683 avaType = null; 684 avaTypes.clear(); 685 nbAvas = 0; 686 normName = ""; 687 upName = ""; 688 normalized = false; 689 h = 0; 690 } 691 692 693 /** 694 * Get the Value of the Ava which type is given as an 695 * argument. 696 * 697 * @param type the type of the NameArgument 698 * @return the Value to be returned, or null if none found. 699 * @throws LdapInvalidDnException if the Rdn is invalid 700 */ 701 public Object getValue( String type ) throws LdapInvalidDnException 702 { 703 // First, let's normalize the type 704 String normalizedType = Strings.lowerCaseAscii( Strings.trim( type ) ); 705 706 if ( schemaManager != null ) 707 { 708 AttributeType attributeType = schemaManager.getAttributeType( normalizedType ); 709 710 if ( attributeType != null ) 711 { 712 normalizedType = attributeType.getOid(); 713 } 714 } 715 716 switch ( nbAvas ) 717 { 718 case 0: 719 return ""; 720 721 case 1: 722 if ( Strings.equals( ava.getNormType(), normalizedType ) ) 723 { 724 return ava.getNormValue().getValue(); 725 } 726 727 return ""; 728 729 default: 730 if ( avaTypes.containsKey( normalizedType ) ) 731 { 732 @SuppressWarnings("unchecked") 733 Collection<Ava> atavList = ( Collection<Ava> ) avaTypes.get( normalizedType ); 734 StringBuffer sb = new StringBuffer(); 735 boolean isFirst = true; 736 737 for ( Ava elem : atavList ) 738 { 739 if ( isFirst ) 740 { 741 isFirst = false; 742 } 743 else 744 { 745 sb.append( ',' ); 746 } 747 748 sb.append( elem.getNormValue() ); 749 } 750 751 return sb.toString(); 752 } 753 754 return ""; 755 } 756 } 757 758 759 /** 760 * Get the Ava which type is given as an argument. If we 761 * have more than one value associated with the type, we will return only 762 * the first one. 763 * 764 * @param type 765 * The type of the NameArgument to be returned 766 * @return The Ava, of null if none is found. 767 */ 768 public Ava getAva( String type ) 769 { 770 // First, let's normalize the type 771 String normalizedType = Strings.lowerCaseAscii( Strings.trim( type ) ); 772 773 switch ( nbAvas ) 774 { 775 case 0: 776 return null; 777 778 case 1: 779 if ( ava.getNormType().equals( normalizedType ) ) 780 { 781 return ava; 782 } 783 784 return null; 785 786 default: 787 if ( avaTypes.containsKey( normalizedType ) ) 788 { 789 @SuppressWarnings("unchecked") 790 Collection<Ava> atavList = ( Collection<Ava> ) avaTypes.get( normalizedType ); 791 return atavList.iterator().next(); 792 } 793 794 return null; 795 } 796 } 797 798 799 /** 800 * Retrieves the components of this Rdn as an iterator of Avas. 801 * The effect on the iterator of updates to this Rdn is undefined. If the 802 * Rdn has zero components, an empty (non-null) iterator is returned. 803 * 804 * @return an iterator of the components of this Rdn, each an Ava 805 */ 806 public Iterator<Ava> iterator() 807 { 808 if ( nbAvas == 1 || nbAvas == 0 ) 809 { 810 return new Iterator<Ava>() 811 { 812 private boolean hasMoreElement = nbAvas == 1; 813 814 815 public boolean hasNext() 816 { 817 return hasMoreElement; 818 } 819 820 821 public Ava next() 822 { 823 Ava obj = ava; 824 hasMoreElement = false; 825 return obj; 826 } 827 828 829 public void remove() 830 { 831 // nothing to do 832 } 833 }; 834 } 835 else 836 { 837 return avas.iterator(); 838 } 839 } 840 841 842 /** 843 * Clone the Rdn 844 * 845 * @return A clone of the current Rdn 846 */ 847 public Rdn clone() 848 { 849 try 850 { 851 Rdn rdn = ( Rdn ) super.clone(); 852 rdn.normalized = normalized; 853 854 // The Ava is immutable. We won't clone it 855 856 switch ( rdn.size() ) 857 { 858 case 0: 859 break; 860 861 case 1: 862 rdn.ava = this.ava.clone(); 863 rdn.avaTypes = avaTypes; 864 break; 865 866 default: 867 // We must duplicate the treeSet and the hashMap 868 rdn.avaTypes = new MultiValueMap(); 869 rdn.avas = new ArrayList<Ava>(); 870 871 for ( Ava currentAva : this.avas ) 872 { 873 rdn.avas.add( currentAva.clone() ); 874 rdn.avaTypes.put( currentAva.getNormType(), currentAva ); 875 } 876 877 break; 878 } 879 880 return rdn; 881 } 882 catch ( CloneNotSupportedException cnse ) 883 { 884 throw new Error( "Assertion failure" ); 885 } 886 } 887 888 889 /** 890 * @return the user provided name 891 */ 892 public String getName() 893 { 894 return upName; 895 } 896 897 898 /** 899 * @return The normalized name 900 */ 901 public String getNormName() 902 { 903 return normName == null ? "" : normName; 904 } 905 906 907 /** 908 * Set the User Provided Name. 909 * 910 * Package private because Rdn is immutable, only used by the Dn parser. 911 * 912 * @param upName the User Provided dame 913 */ 914 void setUpName( String upName ) 915 { 916 this.upName = upName; 917 } 918 919 920 /** 921 * Return the unique Ava, or the first one of we have more 922 * than one 923 * 924 * @return The first Ava of this Rdn 925 */ 926 public Ava getAva() 927 { 928 switch ( nbAvas ) 929 { 930 case 0: 931 return null; 932 933 case 1: 934 return ava; 935 936 default: 937 return avas.get( 0 ); 938 } 939 } 940 941 942 /** 943 * Return the user provided type, or the first one of we have more than one (the lowest) 944 * 945 * @return The first user provided type of this Rdn 946 */ 947 public String getType() 948 { 949 switch ( nbAvas ) 950 { 951 case 0: 952 return null; 953 954 case 1: 955 return ava.getType(); 956 957 default: 958 return avas.get( 0 ).getType(); 959 } 960 } 961 962 963 /** 964 * Return the normalized type, or the first one of we have more than one (the lowest) 965 * 966 * @return The first normalized type of this Rdn 967 */ 968 public String getNormType() 969 { 970 switch ( nbAvas ) 971 { 972 case 0: 973 return null; 974 975 case 1: 976 return ava.getNormType(); 977 978 default: 979 return avas.get( 0 ).getNormType(); 980 } 981 } 982 983 984 /** 985 * Return the User Provided value 986 * 987 * @return The first User provided value of this Rdn 988 */ 989 public Value<?> getValue() 990 { 991 switch ( nbAvas ) 992 { 993 case 0: 994 return null; 995 996 case 1: 997 return ava.getValue(); 998 999 default: 1000 return avas.get( 0 ).getValue(); 1001 } 1002 } 1003 1004 1005 /** 1006 * Return the normalized value, or the first one of we have more than one (the lowest) 1007 * 1008 * @return The first normalized value of this Rdn 1009 */ 1010 public Value<?> getNormValue() 1011 { 1012 switch ( nbAvas ) 1013 { 1014 case 0: 1015 return null; 1016 1017 case 1: 1018 return ava.getNormValue(); 1019 1020 default: 1021 return avas.get( 0 ).getNormValue(); 1022 } 1023 } 1024 1025 1026 /** 1027 * Compares the specified Object with this Rdn for equality. Returns true if 1028 * the given object is also a Rdn and the two Rdns represent the same 1029 * attribute type and value mappings. The order of components in 1030 * multi-valued Rdns is not significant. 1031 * 1032 * @param rdn 1033 * Rdn to be compared for equality with this Rdn 1034 * @return true if the specified object is equal to this Rdn 1035 */ 1036 public boolean equals( Object that ) 1037 { 1038 if ( this == that ) 1039 { 1040 return true; 1041 } 1042 1043 if ( !( that instanceof Rdn ) ) 1044 { 1045 return false; 1046 } 1047 1048 Rdn rdn = ( Rdn ) that; 1049 1050 // Short cut : compare the normalized Rdn 1051 if ( normName.equals( rdn.normName ) ) 1052 { 1053 return true; 1054 } 1055 1056 // Short cut : compare the normalized Rdn 1057 if ( normName.equals( rdn.normName ) ) 1058 { 1059 return true; 1060 } 1061 1062 if ( rdn.nbAvas != nbAvas ) 1063 { 1064 // We don't have the same number of Avas. The Rdn which 1065 // has the higher number of Ava is the one which is 1066 // superior 1067 return false; 1068 } 1069 1070 switch ( nbAvas ) 1071 { 1072 case 0: 1073 return true; 1074 1075 case 1: 1076 return ava.equals( rdn.ava ); 1077 1078 default: 1079 // We have more than one value. We will 1080 // go through all of them. 1081 1082 // the types are already normalized and sorted in the Avas Map 1083 // so we could compare the first element with all of the second 1084 // Ava elemnts, etc. 1085 Iterator<Ava> localIterator = avas.iterator(); 1086 1087 while ( localIterator.hasNext() ) 1088 { 1089 Iterator<Ava> paramIterator = rdn.avas.iterator(); 1090 1091 Ava localAva = localIterator.next(); 1092 boolean equals = false; 1093 1094 while ( paramIterator.hasNext() ) 1095 { 1096 Ava paramAva = paramIterator.next(); 1097 1098 if ( localAva.equals( paramAva ) ) 1099 { 1100 equals = true; 1101 break; 1102 } 1103 } 1104 1105 if ( !equals ) 1106 { 1107 return false; 1108 } 1109 } 1110 1111 return true; 1112 } 1113 } 1114 1115 1116 /** 1117 * Get the number of Avas of this Rdn 1118 * 1119 * @return The number of Avas in this Rdn 1120 */ 1121 public int size() 1122 { 1123 return nbAvas; 1124 } 1125 1126 1127 /** 1128 * Unescape the given string according to RFC 2253 If in <string> form, a 1129 * LDAP string representation asserted value can be obtained by replacing 1130 * (left-to-right, non-recursively) each <pair> appearing in the <string> as 1131 * follows: replace <ESC><ESC> with <ESC>; replace <ESC><special> with 1132 * <special>; replace <ESC><hexpair> with the octet indicated by the 1133 * <hexpair> If in <hexstring> form, a BER representation can be obtained 1134 * from converting each <hexpair> of the <hexstring> to the octet indicated 1135 * by the <hexpair> 1136 * 1137 * @param value The value to be unescaped 1138 * @return Returns a string value as a String, and a binary value as a byte 1139 * array. 1140 * @throws IllegalArgumentException When an Illegal value is provided. 1141 */ 1142 public static Object unescapeValue( String value ) throws IllegalArgumentException 1143 { 1144 if ( Strings.isEmpty( value ) ) 1145 { 1146 return ""; 1147 } 1148 1149 char[] chars = value.toCharArray(); 1150 1151 // If the value is contained into double quotes, return it as is. 1152 if ( ( chars[0] == '\"' ) && ( chars[chars.length - 1] == '\"' ) ) 1153 { 1154 return value; 1155 } 1156 1157 if ( chars[0] == '#' ) 1158 { 1159 if ( chars.length == 1 ) 1160 { 1161 // The value is only containing a # 1162 return StringConstants.EMPTY_BYTES; 1163 } 1164 1165 if ( ( chars.length % 2 ) != 1 ) 1166 { 1167 throw new IllegalArgumentException( I18n.err( I18n.ERR_04213 ) ); 1168 } 1169 1170 // HexString form 1171 byte[] hexValue = new byte[( chars.length - 1 ) / 2]; 1172 int pos = 0; 1173 1174 for ( int i = 1; i < chars.length; i += 2 ) 1175 { 1176 if ( Chars.isHex( chars, i ) && Chars.isHex( chars, i + 1 ) ) 1177 { 1178 hexValue[pos++] = Hex.getHexValue( chars[i], chars[i + 1] ); 1179 } 1180 else 1181 { 1182 throw new IllegalArgumentException( I18n.err( I18n.ERR_04214 ) ); 1183 } 1184 } 1185 1186 return hexValue; 1187 } 1188 else 1189 { 1190 boolean escaped = false; 1191 boolean isHex = false; 1192 byte pair = -1; 1193 int pos = 0; 1194 1195 byte[] bytes = new byte[chars.length * 6]; 1196 1197 for ( int i = 0; i < chars.length; i++ ) 1198 { 1199 if ( escaped ) 1200 { 1201 escaped = false; 1202 1203 switch ( chars[i] ) 1204 { 1205 case '\\': 1206 case '"': 1207 case '+': 1208 case ',': 1209 case ';': 1210 case '<': 1211 case '>': 1212 case '#': 1213 case '=': 1214 case ' ': 1215 bytes[pos++] = ( byte ) chars[i]; 1216 break; 1217 1218 default: 1219 if ( Chars.isHex( chars, i ) ) 1220 { 1221 isHex = true; 1222 pair = ( ( byte ) ( Hex.getHexValue( chars[i] ) << 4 ) ); 1223 } 1224 1225 break; 1226 } 1227 } 1228 else 1229 { 1230 if ( isHex ) 1231 { 1232 if ( Chars.isHex( chars, i ) ) 1233 { 1234 pair += Hex.getHexValue( chars[i] ); 1235 bytes[pos++] = pair; 1236 isHex = false; 1237 } 1238 } 1239 else 1240 { 1241 switch ( chars[i] ) 1242 { 1243 case '\\': 1244 escaped = true; 1245 break; 1246 1247 // We must not have a special char 1248 // Specials are : '"', '+', ',', ';', '<', '>', ' ', 1249 // '#' and '=' 1250 case '"': 1251 case '+': 1252 case ',': 1253 case ';': 1254 case '<': 1255 case '>': 1256 case '#': 1257 if ( i != 0 ) 1258 { 1259 // '#' are allowed if not in first position 1260 bytes[pos++] = '#'; 1261 break; 1262 } 1263 case '=': 1264 throw new IllegalArgumentException( I18n.err( I18n.ERR_04215 ) ); 1265 1266 case ' ': 1267 if ( ( i == 0 ) || ( i == chars.length - 1 ) ) 1268 { 1269 throw new IllegalArgumentException( I18n.err( I18n.ERR_04215 ) ); 1270 } 1271 else 1272 { 1273 bytes[pos++] = ' '; 1274 break; 1275 } 1276 1277 default: 1278 if ( ( chars[i] >= 0 ) && ( chars[i] < 128 ) ) 1279 { 1280 bytes[pos++] = ( byte ) chars[i]; 1281 } 1282 else 1283 { 1284 byte[] result = Unicode.charToBytes( chars[i] ); 1285 System.arraycopy( result, 0, bytes, pos, result.length ); 1286 pos += result.length; 1287 } 1288 1289 break; 1290 } 1291 } 1292 } 1293 } 1294 1295 return Strings.utf8ToString( bytes, pos ); 1296 } 1297 } 1298 1299 1300 /** 1301 * Transform a value in a String, accordingly to RFC 2253 1302 * 1303 * @param value The attribute value to be escaped 1304 * @return The escaped string value. 1305 */ 1306 public static String escapeValue( String value ) 1307 { 1308 if ( Strings.isEmpty( value ) ) 1309 { 1310 return ""; 1311 } 1312 1313 char[] chars = value.toCharArray(); 1314 char[] newChars = new char[chars.length * 3]; 1315 int pos = 0; 1316 1317 for ( int i = 0; i < chars.length; i++ ) 1318 { 1319 switch ( chars[i] ) 1320 { 1321 case ' ': 1322 if ( ( i > 0 ) && ( i < chars.length - 1 ) ) 1323 { 1324 newChars[pos++] = chars[i]; 1325 } 1326 else 1327 { 1328 newChars[pos++] = '\\'; 1329 newChars[pos++] = chars[i]; 1330 } 1331 1332 break; 1333 1334 case '#': 1335 if ( i != 0 ) 1336 { 1337 newChars[pos++] = chars[i]; 1338 } 1339 else 1340 { 1341 newChars[pos++] = '\\'; 1342 newChars[pos++] = chars[i]; 1343 } 1344 1345 break; 1346 1347 case '"': 1348 case '+': 1349 case ',': 1350 case ';': 1351 case '=': 1352 case '<': 1353 case '>': 1354 case '\\': 1355 newChars[pos++] = '\\'; 1356 newChars[pos++] = chars[i]; 1357 break; 1358 1359 case 0x7F: 1360 newChars[pos++] = '\\'; 1361 newChars[pos++] = '7'; 1362 newChars[pos++] = 'F'; 1363 break; 1364 1365 case 0x00: 1366 case 0x01: 1367 case 0x02: 1368 case 0x03: 1369 case 0x04: 1370 case 0x05: 1371 case 0x06: 1372 case 0x07: 1373 case 0x08: 1374 case 0x09: 1375 case 0x0A: 1376 case 0x0B: 1377 case 0x0C: 1378 case 0x0D: 1379 case 0x0E: 1380 case 0x0F: 1381 newChars[pos++] = '\\'; 1382 newChars[pos++] = '0'; 1383 newChars[pos++] = Strings.dumpHex( ( byte ) ( chars[i] & 0x0F ) ); 1384 break; 1385 1386 case 0x10: 1387 case 0x11: 1388 case 0x12: 1389 case 0x13: 1390 case 0x14: 1391 case 0x15: 1392 case 0x16: 1393 case 0x17: 1394 case 0x18: 1395 case 0x19: 1396 case 0x1A: 1397 case 0x1B: 1398 case 0x1C: 1399 case 0x1D: 1400 case 0x1E: 1401 case 0x1F: 1402 newChars[pos++] = '\\'; 1403 newChars[pos++] = '1'; 1404 newChars[pos++] = Strings.dumpHex( ( byte ) ( chars[i] & 0x0F ) ); 1405 break; 1406 1407 default: 1408 newChars[pos++] = chars[i]; 1409 break; 1410 } 1411 } 1412 1413 return new String( newChars, 0, pos ); 1414 } 1415 1416 1417 /** 1418 * Transform a value in a String, accordingly to RFC 2253 1419 * 1420 * @param attrValue 1421 * The attribute value to be escaped 1422 * @return The escaped string value. 1423 */ 1424 public static String escapeValue( byte[] attrValue ) 1425 { 1426 if ( Strings.isEmpty( attrValue ) ) 1427 { 1428 return ""; 1429 } 1430 1431 String value = Strings.utf8ToString( attrValue ); 1432 1433 return escapeValue( value ); 1434 } 1435 1436 1437 /** 1438 * Tells if the Rdn is schema aware. 1439 * 1440 * @return <code>true</code> if the Rdn is schema aware 1441 */ 1442 public boolean isSchemaAware() 1443 { 1444 return schemaManager != null; 1445 } 1446 1447 1448 /** 1449 * Validate a NameComponent : <br> 1450 * <p> 1451 * <name-component> ::= <attributeType> <spaces> '=' 1452 * <spaces> <attributeValue> <nameComponents> 1453 * </p> 1454 * 1455 * @param dn The string to parse 1456 * @return <code>true</code> if the Rdn is valid 1457 */ 1458 public static boolean isValid( String dn ) 1459 { 1460 Rdn rdn = new Rdn(); 1461 try 1462 { 1463 parse( dn, rdn ); 1464 return true; 1465 } 1466 catch ( LdapInvalidDnException e ) 1467 { 1468 return false; 1469 } 1470 } 1471 1472 1473 /** 1474 * Parse a NameComponent : <br> 1475 * <p> 1476 * <name-component> ::= <attributeType> <spaces> '=' 1477 * <spaces> <attributeValue> <nameComponents> 1478 * </p> 1479 * 1480 * @param dn The String to parse 1481 * @param rdn The Rdn to fill. Beware that if the Rdn is not empty, the new 1482 * AttributeTypeAndValue will be added. 1483 * @throws LdapInvalidDnException If the NameComponent is invalid 1484 */ 1485 private static void parse( String dn, Rdn rdn ) throws LdapInvalidDnException 1486 { 1487 try 1488 { 1489 FastDnParser.parseRdn( dn, rdn ); 1490 } 1491 catch ( TooComplexDnException e ) 1492 { 1493 rdn.clear(); 1494 new ComplexDnParser().parseRdn( dn, rdn ); 1495 } 1496 } 1497 1498 1499 /** 1500 * Gets the hashcode of this rdn. 1501 * 1502 * @see java.lang.Object#hashCode() 1503 * @return the instance's hash code 1504 */ 1505 public int hashCode() 1506 { 1507 if ( h == 0 ) 1508 { 1509 h = 37; 1510 1511 switch ( nbAvas ) 1512 { 1513 case 0: 1514 // An empty Rdn 1515 break; 1516 1517 case 1: 1518 // We have a single Ava 1519 h = h * 17 + ava.hashCode(); 1520 break; 1521 1522 default: 1523 // We have more than one Ava 1524 1525 for ( Ava ata : avas ) 1526 { 1527 h = h * 17 + ata.hashCode(); 1528 } 1529 1530 break; 1531 } 1532 } 1533 1534 return h; 1535 } 1536 1537 1538 /** 1539 * Serialize a RDN into a byte[] 1540 * 1541 * @return a byte[] containing a RDN 1542 */ 1543 public int serialize( byte[] buffer, int pos ) throws IOException 1544 { 1545 int length = 4 + 4; // The nbAvas and the HashCode length 1546 1547 // The NnbAvas 1548 pos = Serialize.serialize( nbAvas, buffer, pos ); 1549 1550 // The upName 1551 byte[] upNameBytes = Strings.getBytesUtf8( upName ); 1552 length += 4 + upNameBytes.length; 1553 1554 byte[] normNameBytes = Strings.EMPTY_BYTES; 1555 length += 4; 1556 1557 if ( !upName.equals( normName ) ) 1558 { 1559 normNameBytes = Strings.getBytesUtf8( normName ); 1560 length += 4 + normNameBytes.length; 1561 } 1562 1563 // Check that we will be able to store the data in the buffer 1564 if ( buffer.length - pos < length ) 1565 { 1566 throw new ArrayIndexOutOfBoundsException(); 1567 } 1568 1569 // Write the upName 1570 pos = Serialize.serialize( upNameBytes, buffer, pos ); 1571 1572 // Write the normName 1573 pos = Serialize.serialize( normNameBytes, buffer, pos ); 1574 1575 // Write the AVAs 1576 switch ( nbAvas ) 1577 { 1578 case 0: 1579 break; 1580 1581 case 1: 1582 pos = ava.serialize( buffer, pos ); 1583 1584 break; 1585 1586 default: 1587 for ( Ava localAva : avas ) 1588 { 1589 pos = localAva.serialize( buffer, pos ); 1590 } 1591 1592 break; 1593 } 1594 1595 // The hash code 1596 pos = Serialize.serialize( h, buffer, pos ); 1597 1598 return pos; 1599 } 1600 1601 1602 /** 1603 * Deserialize a RDN from a byte[], starting at a given position 1604 * 1605 * @param buffer The buffer containing the RDN 1606 * @param pos The position in the buffer 1607 * @return The new position 1608 * @throws IOException If the serialized value is not a RDN 1609 * @throws LdapInvalidAttributeValueException If the serialized RDN is invalid 1610 */ 1611 public int deserialize( byte[] buffer, int pos ) throws IOException, LdapInvalidAttributeValueException 1612 { 1613 if ( ( pos < 0 ) || ( pos >= buffer.length ) ) 1614 { 1615 throw new ArrayIndexOutOfBoundsException(); 1616 } 1617 1618 // Read the nbAvas 1619 nbAvas = Serialize.deserializeInt( buffer, pos ); 1620 pos += 4; 1621 1622 // Read the upName 1623 byte[] upNameBytes = Serialize.deserializeBytes( buffer, pos ); 1624 pos += 4 + upNameBytes.length; 1625 upName = Strings.utf8ToString( upNameBytes ); 1626 1627 // Read the normName 1628 byte[] normNameBytes = Serialize.deserializeBytes( buffer, pos ); 1629 pos += 4 + normNameBytes.length; 1630 1631 if ( normNameBytes.length == 0 ) 1632 { 1633 normName = upName; 1634 } 1635 else 1636 { 1637 normName = Strings.utf8ToString( normNameBytes ); 1638 } 1639 1640 // Read the AVAs 1641 switch ( nbAvas ) 1642 { 1643 case 0: 1644 break; 1645 1646 case 1: 1647 ava = new Ava( schemaManager ); 1648 pos = ava.deserialize( buffer, pos ); 1649 avaType = ava.getNormType(); 1650 1651 break; 1652 1653 default: 1654 avas = new ArrayList<Ava>(); 1655 1656 avaTypes = new MultiValueMap(); 1657 1658 for ( int i = 0; i < nbAvas; i++ ) 1659 { 1660 Ava ava = new Ava( schemaManager ); 1661 pos = ava.deserialize( buffer, pos ); 1662 avas.add( ava ); 1663 avaTypes.put( ava.getNormType(), ava ); 1664 } 1665 1666 ava = null; 1667 avaType = null; 1668 1669 break; 1670 } 1671 1672 // Read the hashCode 1673 h = Serialize.deserializeInt( buffer, pos ); 1674 pos += 4; 1675 1676 return pos; 1677 } 1678 1679 1680 /** 1681 * A Rdn is composed of on to many Avas (AttributeType And Value). 1682 * We should write all those Avas sequencially, following the 1683 * structure : 1684 * <ul> 1685 * <li> 1686 * <b>parentId</b> The parent entry's Id 1687 * </li> 1688 * <li> 1689 * <b>nbAvas</b> The number of Avas to write. Can't be 0. 1690 * </li> 1691 * <li> 1692 * <b>upName</b> The User provided Rdn 1693 * </li> 1694 * <li> 1695 * <b>normName</b> The normalized Rdn. It can be empty if the normalized 1696 * name equals the upName. 1697 * </li> 1698 * <li> 1699 * <b>Avas</b> 1700 * </li> 1701 * </ul> 1702 * <br/> 1703 * For each Ava : 1704 * <ul> 1705 * <li> 1706 * <b>start</b> The position of this Ava in the upName string 1707 * </li> 1708 * <li> 1709 * <b>length</b> The Ava user provided length 1710 * </li> 1711 * <li> 1712 * <b>Call the Ava write method</b> The Ava itself 1713 * </li> 1714 * </ul> 1715 * 1716 * @see Externalizable#readExternal(ObjectInput) 1717 * @param out The stream into which the serialized Rdn will be put 1718 * @throws IOException If the stream can't be written 1719 */ 1720 public void writeExternal( ObjectOutput out ) throws IOException 1721 { 1722 out.writeInt( nbAvas ); 1723 out.writeUTF( upName ); 1724 1725 if ( upName.equals( normName ) ) 1726 { 1727 out.writeUTF( "" ); 1728 } 1729 else 1730 { 1731 out.writeUTF( normName ); 1732 } 1733 1734 switch ( nbAvas ) 1735 { 1736 case 0: 1737 break; 1738 1739 case 1: 1740 ava.writeExternal( out ); 1741 break; 1742 1743 default: 1744 for ( Ava localAva : avas ) 1745 { 1746 localAva.writeExternal( out ); 1747 } 1748 1749 break; 1750 } 1751 1752 out.writeInt( h ); 1753 1754 out.flush(); 1755 } 1756 1757 1758 /** 1759 * We read back the data to create a new RDB. The structure 1760 * read is exposed in the {@link Rdn#writeExternal(ObjectOutput)} 1761 * method 1762 * 1763 * @see Externalizable#readExternal(ObjectInput) 1764 * @param in The input stream from which the Rdn will be read 1765 * @throws IOException If we can't read from the input stream 1766 * @throws ClassNotFoundException If we can't create a new Rdn 1767 */ 1768 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException 1769 { 1770 // Read the Ava number 1771 nbAvas = in.readInt(); 1772 1773 // Read the UPName 1774 upName = in.readUTF(); 1775 1776 // Read the normName 1777 normName = in.readUTF(); 1778 1779 if ( Strings.isEmpty( normName ) ) 1780 { 1781 normName = upName; 1782 } 1783 1784 switch ( nbAvas ) 1785 { 1786 case 0: 1787 ava = null; 1788 break; 1789 1790 case 1: 1791 ava = new Ava( schemaManager ); 1792 ava.readExternal( in ); 1793 avaType = ava.getNormType(); 1794 1795 break; 1796 1797 default: 1798 avas = new ArrayList<Ava>(); 1799 1800 avaTypes = new MultiValueMap(); 1801 1802 for ( int i = 0; i < nbAvas; i++ ) 1803 { 1804 Ava ava = new Ava( schemaManager ); 1805 ava.readExternal( in ); 1806 avas.add( ava ); 1807 avaTypes.put( ava.getNormType(), ava ); 1808 } 1809 1810 ava = null; 1811 avaType = null; 1812 1813 break; 1814 } 1815 1816 h = in.readInt(); 1817 } 1818 1819 1820 public int compareTo( Rdn arg0 ) 1821 { 1822 // TODO Auto-generated method stub 1823 return 0; 1824 } 1825 1826 1827 /** 1828 * @return a String representation of the Rdn. The caller will get back the user 1829 * provided Rdn 1830 */ 1831 public String toString() 1832 { 1833 return upName == null ? "" : upName; 1834 } 1835}