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 */ 020 package org.apache.directory.shared.ldap.name; 021 022 023 import java.io.Externalizable; 024 import java.io.IOException; 025 import java.io.ObjectInput; 026 import java.io.ObjectOutput; 027 import java.util.Collection; 028 import java.util.Iterator; 029 import java.util.Map; 030 import java.util.Set; 031 import java.util.TreeSet; 032 033 import org.apache.commons.collections.MultiMap; 034 import org.apache.commons.collections.map.MultiValueMap; 035 import org.apache.directory.shared.i18n.I18n; 036 import org.apache.directory.shared.ldap.entry.StringValue; 037 import org.apache.directory.shared.ldap.entry.Value; 038 import org.apache.directory.shared.ldap.exception.LdapException; 039 import org.apache.directory.shared.ldap.exception.LdapInvalidDnException; 040 import org.apache.directory.shared.ldap.schema.normalizers.OidNormalizer; 041 import org.apache.directory.shared.ldap.util.StringTools; 042 import org.slf4j.Logger; 043 import org.slf4j.LoggerFactory; 044 045 046 /** 047 * This class store the name-component part or the following BNF grammar (as of 048 * RFC2253, par. 3, and RFC1779, fig. 1) : <br> - <name-component> ::= 049 * <attributeType> <spaces> '=' <spaces> 050 * <attributeValue> <attributeTypeAndValues> <br> - 051 * <attributeTypeAndValues> ::= <spaces> '+' <spaces> 052 * <attributeType> <spaces> '=' <spaces> 053 * <attributeValue> <attributeTypeAndValues> | e <br> - 054 * <attributeType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] 055 * <digits> <oids> | [0-9] <digits> <oids> <br> - 056 * <keychars> ::= [a-zA-Z] <keychars> | [0-9] <keychars> | '-' 057 * <keychars> | e <br> - <oidPrefix> ::= 'OID.' | 'oid.' | e <br> - 058 * <oids> ::= '.' [0-9] <digits> <oids> | e <br> - 059 * <attributeValue> ::= <pairs-or-strings> | '#' <hexstring> 060 * |'"' <quotechar-or-pairs> '"' <br> - <pairs-or-strings> ::= '\' 061 * <pairchar> <pairs-or-strings> | <stringchar> 062 * <pairs-or-strings> | e <br> - <quotechar-or-pairs> ::= 063 * <quotechar> <quotechar-or-pairs> | '\' <pairchar> 064 * <quotechar-or-pairs> | e <br> - <pairchar> ::= ',' | '=' | '+' | 065 * '<' | '>' | '#' | ';' | '\' | '"' | [0-9a-fA-F] [0-9a-fA-F] <br> - 066 * <hexstring> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> <br> - 067 * <hexpairs> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> | e <br> - 068 * <digits> ::= [0-9] <digits> | e <br> - <stringchar> ::= 069 * [0x00-0xFF] - [,=+<>#;\"\n\r] <br> - <quotechar> ::= [0x00-0xFF] - 070 * [\"] <br> - <separator> ::= ',' | ';' <br> - <spaces> ::= ' ' 071 * <spaces> | e <br> 072 * <br> 073 * A RDN is a part of a DN. It can be composed of many types, as in the RDN 074 * following RDN :<br> 075 * ou=value + cn=other value<br> 076 * <br> 077 * or <br> 078 * ou=value + ou=another value<br> 079 * <br> 080 * In this case, we have to store an 'ou' and a 'cn' in the RDN.<br> 081 * <br> 082 * The types are case insensitive. <br> 083 * Spaces before and after types and values are not stored.<br> 084 * Spaces before and after '+' are not stored.<br> 085 * <br> 086 * Thus, we can consider that the following RDNs are equals :<br> 087 * <br> 088 * 'ou=test 1'<br> ' ou=test 1'<br> 089 * 'ou =test 1'<br> 090 * 'ou= test 1'<br> 091 * 'ou=test 1 '<br> ' ou = test 1 '<br> 092 * <br> 093 * So are the following :<br> 094 * <br> 095 * 'ou=test 1+cn=test 2'<br> 096 * 'ou = test 1 + cn = test 2'<br> ' ou =test 1+ cn =test 2 ' <br> 097 * 'cn = test 2 +ou = test 1'<br> 098 * <br> 099 * but the following are not equal :<br> 100 * 'ou=test 1' <br> 101 * 'ou=test 1'<br> 102 * because we have more than one spaces inside the value.<br> 103 * <br> 104 * The Rdn is composed of one or more AttributeTypeAndValue (atav) Those atavs 105 * are ordered in the alphabetical natural order : a < b < c ... < z As the type 106 * are not case sensitive, we can say that a = A 107 * 108 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 109 * @version $Rev: 928945 $, $Date: 2010-03-30 01:59:49 +0200 (Tue, 30 Mar 2010) $ 110 */ 111 public class RDN implements Cloneable, Comparable<RDN>, Externalizable, Iterable<AVA> 112 { 113 /** The LoggerFactory used by this class */ 114 protected static final Logger LOG = LoggerFactory.getLogger( RDN.class ); 115 116 /** An empty RDN */ 117 public static final RDN EMPTY_RDN = new RDN(); 118 119 /** 120 * Declares the Serial Version Uid. 121 * 122 * @see <a 123 * href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always 124 * Declare Serial Version Uid</a> 125 */ 126 private static final long serialVersionUID = 1L; 127 128 /** The User Provided RDN */ 129 private String upName = null; 130 131 /** The normalized RDN */ 132 private String normName = null; 133 134 /** The starting position of this RDN in the given string from which 135 * we have extracted the upName */ 136 private int start; 137 138 /** The length of this RDN upName */ 139 private int length; 140 141 /** 142 * Stores all couple type = value. We may have more than one type, if the 143 * '+' character appears in the AttributeTypeAndValue. This is a TreeSet, 144 * because we want the ATAVs to be sorted. An atav may contain more than one 145 * value. In this case, the values are String stored in a List. 146 */ 147 private Set<AVA> atavs = null; 148 149 /** 150 * We also keep a set of types, in order to use manipulations. A type is 151 * connected with the atav it represents. 152 * 153 * Note : there is no Generic available classes in commons-collection... 154 */ 155 private MultiMap atavTypes = new MultiValueMap(); 156 157 /** 158 * We keep the type for a single valued RDN, to avoid the creation of an HashMap 159 */ 160 private String atavType = null; 161 162 /** 163 * A simple AttributeTypeAndValue is used to store the Rdn for the simple 164 * case where we only have a single type=value. This will be 99.99% the 165 * case. This avoids the creation of a HashMap. 166 */ 167 protected AVA atav = null; 168 169 /** 170 * The number of atavs. We store this number here to avoid complex 171 * manipulation of atav and atavs 172 */ 173 private int nbAtavs = 0; 174 175 /** CompareTo() results */ 176 public static final int UNDEFINED = Integer.MAX_VALUE; 177 178 /** Constant used in comparisons */ 179 public static final int SUPERIOR = 1; 180 181 /** Constant used in comparisons */ 182 public static final int INFERIOR = -1; 183 184 /** Constant used in comparisons */ 185 public static final int EQUAL = 0; 186 187 188 /** 189 * A empty constructor. 190 */ 191 public RDN() 192 { 193 // Don't waste space... This is not so often we have multiple 194 // name-components in a RDN... So we won't initialize the Map and the 195 // treeSet. 196 upName = ""; 197 normName = ""; 198 } 199 200 201 /** 202 * A constructor that parse a String representing a RDN. 203 * 204 * @param rdn The String containing the RDN to parse 205 * @throws LdapInvalidDnException If the RDN is invalid 206 */ 207 public RDN( String rdn ) throws LdapInvalidDnException 208 { 209 start = 0; 210 211 if ( StringTools.isNotEmpty( rdn ) ) 212 { 213 // Parse the string. The Rdn will be updated. 214 RdnParser.parse( rdn, this ); 215 216 // create the internal normalized form 217 // and store the user provided form 218 normalize(); 219 upName = rdn; 220 length = rdn.length(); 221 } 222 else 223 { 224 upName = ""; 225 normName = ""; 226 length = 0; 227 } 228 } 229 230 231 /** 232 * A constructor that constructs a RDN from a type and a value. Constructs 233 * an Rdn from the given attribute type and value. The string attribute 234 * values are not interpreted as RFC 2253 formatted RDN strings. That is, 235 * the values are used literally (not parsed) and assumed to be un-escaped. 236 * 237 * @param upType The user provided type of the RDN 238 * @param upValue The user provided value of the RDN 239 * @param normType The normalized provided type of the RDN 240 * @param normValue The normalized provided value of the RDN 241 * @throws LdapInvalidDnException If the RDN is invalid 242 */ 243 public RDN( String upType, String normType, String upValue, String normValue ) throws LdapInvalidDnException 244 { 245 addAttributeTypeAndValue( upType, normType, new StringValue( upValue ), new StringValue( normValue ) ); 246 247 upName = upType + '=' + upValue; 248 start = 0; 249 length = upName.length(); 250 // create the internal normalized form 251 normalize(); 252 } 253 254 255 /** 256 * A constructor that constructs a RDN from a type and a value. Constructs 257 * an Rdn from the given attribute type and value. The string attribute 258 * values are not interpreted as RFC 2253 formatted RDN strings. That is, 259 * the values are used literally (not parsed) and assumed to be un-escaped. 260 * 261 * @param upType The user provided type of the RDN 262 * @param upValue The user provided value of the RDN 263 * @throws LdapInvalidDnException If the RDN is invalid 264 */ 265 public RDN( String upType, String upValue ) throws LdapInvalidDnException 266 { 267 addAttributeTypeAndValue( upType, upType, new StringValue( upValue ), new StringValue( upValue ) ); 268 269 upName = upType + '=' + upValue; 270 start = 0; 271 length = upName.length(); 272 // create the internal normalized form 273 normalize(); 274 } 275 276 277 /** 278 * A constructor that constructs a RDN from a type, a position and a length. 279 * 280 * @param start The starting point for this RDN in the user provided DN 281 * @param length The RDN's length 282 * @param upName The user provided name 283 * @param normName the normalized name 284 */ 285 RDN( int start, int length, String upName, String normName ) 286 { 287 this.start = 0; 288 this.length = length; 289 this.upName = upName; 290 this.normName = normName; 291 } 292 293 294 /** 295 * Constructs an Rdn from the given rdn. The contents of the rdn are simply 296 * copied into the newly created 297 * 298 * @param rdn 299 * The non-null Rdn to be copied. 300 */ 301 public RDN( RDN rdn ) 302 { 303 nbAtavs = rdn.getNbAtavs(); 304 this.normName = rdn.normName; 305 this.upName = rdn.getName(); 306 this.start = rdn.start; 307 this.length = rdn.length; 308 309 switch ( rdn.getNbAtavs() ) 310 { 311 case 0: 312 return; 313 314 case 1: 315 this.atav = ( AVA ) rdn.atav.clone(); 316 return; 317 318 default: 319 // We must duplicate the treeSet and the hashMap 320 atavs = new TreeSet<AVA>(); 321 atavTypes = new MultiValueMap(); 322 323 for ( AVA currentAtav : rdn.atavs ) 324 { 325 atavs.add( ( AVA ) currentAtav.clone() ); 326 atavTypes.put( currentAtav.getNormType(), currentAtav ); 327 } 328 329 return; 330 } 331 } 332 333 334 /** 335 * Transform the external representation of the current RDN to an internal 336 * normalized form where : 337 * - types are trimmed and lower cased 338 * - values are trimmed and lower cased 339 */ 340 // WARNING : The protection level is left unspecified on purpose. 341 // We need this method to be visible from the DnParser class, but not 342 // from outside this package. 343 /* Unspecified protection */void normalize() 344 { 345 switch ( nbAtavs ) 346 { 347 case 0: 348 // An empty RDN 349 normName = ""; 350 break; 351 352 case 1: 353 // We have a single AttributeTypeAndValue 354 // We will trim and lowercase type and value. 355 if ( !atav.getNormValue().isBinary() ) 356 { 357 normName = atav.getNormName(); 358 } 359 else 360 { 361 normName = atav.getNormType() + "=#" + StringTools.dumpHexPairs( atav.getNormValue().getBytes() ); 362 } 363 364 break; 365 366 default: 367 // We have more than one AttributeTypeAndValue 368 StringBuffer sb = new StringBuffer(); 369 370 boolean isFirst = true; 371 372 for ( AVA ata : atavs ) 373 { 374 if ( isFirst ) 375 { 376 isFirst = false; 377 } 378 else 379 { 380 sb.append( '+' ); 381 } 382 383 sb.append( ata.normalize() ); 384 } 385 386 normName = sb.toString(); 387 break; 388 } 389 } 390 391 392 /** 393 * Transform a RDN by changing the value to its OID counterpart and 394 * normalizing the value accordingly to its type. 395 * 396 * @param rdn The RDN to modify. 397 * @param oidsMap The map of all existing oids and normalizer. 398 * @throws LdapException If the RDN is invalid. 399 */ 400 public RDN normalize( Map<String, OidNormalizer> oidsMap ) throws LdapInvalidDnException 401 { 402 String upName = getName(); 403 DN.rdnOidToName( this, oidsMap ); 404 normalize(); 405 this.upName = upName; 406 407 408 return this; 409 } 410 411 412 413 /** 414 * Add a AttributeTypeAndValue to the current RDN 415 * 416 * @param upType The user provided type of the added RDN. 417 * @param type The normalized provided type of the added RDN. 418 * @param upValue The user provided value of the added RDN 419 * @param value The normalized provided value of the added RDN 420 * @throws LdapInvalidDnException 421 * If the RDN is invalid 422 */ 423 // WARNING : The protection level is left unspecified intentionally. 424 // We need this method to be visible from the DnParser class, but not 425 // from outside this package. 426 /* Unspecified protection */void addAttributeTypeAndValue( String upType, String type, Value<?> upValue, 427 Value<?> value ) throws LdapInvalidDnException 428 { 429 // First, let's normalize the type 430 String normalizedType = StringTools.lowerCaseAscii( type ); 431 Value<?> normalizedValue = value; 432 433 switch ( nbAtavs ) 434 { 435 case 0: 436 // This is the first AttributeTypeAndValue. Just stores it. 437 atav = new AVA( upType, type, upValue, normalizedValue ); 438 nbAtavs = 1; 439 atavType = normalizedType; 440 return; 441 442 case 1: 443 // We already have an atav. We have to put it in the HashMap 444 // before adding a new one. 445 // First, create the HashMap, 446 atavs = new TreeSet<AVA>(); 447 448 // and store the existing AttributeTypeAndValue into it. 449 atavs.add( atav ); 450 atavTypes = new MultiValueMap(); 451 atavTypes.put( atavType, atav ); 452 453 atav = null; 454 455 // Now, fall down to the commmon case 456 // NO BREAK !!! 457 458 default: 459 // add a new AttributeTypeAndValue 460 AVA newAtav = new AVA( upType, type, upValue, normalizedValue ); 461 atavs.add( newAtav ); 462 atavTypes.put( normalizedType, newAtav ); 463 464 nbAtavs++; 465 break; 466 467 } 468 } 469 470 471 /** 472 * Add a AttributeTypeAndValue to the current RDN 473 * 474 * @param value The added AttributeTypeAndValue 475 */ 476 // WARNING : The protection level is left unspecified intentionnaly. 477 // We need this method to be visible from the DnParser class, but not 478 // from outside this package. 479 /* Unspecified protection */void addAttributeTypeAndValue( AVA value ) 480 { 481 String normalizedType = value.getNormType(); 482 483 switch ( nbAtavs ) 484 { 485 case 0: 486 // This is the first AttributeTypeAndValue. Just stores it. 487 atav = value; 488 nbAtavs = 1; 489 atavType = normalizedType; 490 return; 491 492 case 1: 493 // We already have an atav. We have to put it in the HashMap 494 // before adding a new one. 495 // First, create the HashMap, 496 atavs = new TreeSet<AVA>(); 497 498 // and store the existing AttributeTypeAndValue into it. 499 atavs.add( atav ); 500 atavTypes = new MultiValueMap(); 501 atavTypes.put( atavType, atav ); 502 503 this.atav = null; 504 505 // Now, fall down to the commmon case 506 // NO BREAK !!! 507 508 default: 509 // add a new AttributeTypeAndValue 510 atavs.add( value ); 511 atavTypes.put( normalizedType, value ); 512 513 nbAtavs++; 514 break; 515 516 } 517 } 518 519 520 /** 521 * Clear the RDN, removing all the AttributeTypeAndValues. 522 */ 523 public void clear() 524 { 525 atav = null; 526 atavs = null; 527 atavType = null; 528 atavTypes.clear(); 529 nbAtavs = 0; 530 normName = ""; 531 upName = ""; 532 start = -1; 533 length = 0; 534 } 535 536 537 /** 538 * Get the Value of the AttributeTypeAndValue which type is given as an 539 * argument. 540 * 541 * @param type 542 * The type of the NameArgument 543 * @return The Value to be returned, or null if none found. 544 * @throws LdapInvalidDnException 545 */ 546 public Object getValue( String type ) throws LdapInvalidDnException 547 { 548 // First, let's normalize the type 549 String normalizedType = StringTools.lowerCaseAscii( StringTools.trim( type ) ); 550 551 switch ( nbAtavs ) 552 { 553 case 0: 554 return ""; 555 556 case 1: 557 if ( StringTools.equals( atav.getNormType(), normalizedType ) ) 558 { 559 return atav.getNormValue().get(); 560 } 561 562 return ""; 563 564 default: 565 if ( atavTypes.containsKey( normalizedType ) ) 566 { 567 Collection<AVA> atavList = ( Collection<AVA> ) atavTypes.get( normalizedType ); 568 StringBuffer sb = new StringBuffer(); 569 boolean isFirst = true; 570 571 for ( AVA elem : atavList ) 572 { 573 if ( isFirst ) 574 { 575 isFirst = false; 576 } 577 else 578 { 579 sb.append( ',' ); 580 } 581 582 sb.append( elem.getNormValue() ); 583 } 584 585 return sb.toString(); 586 } 587 588 return ""; 589 } 590 } 591 592 593 /** 594 * Get the start position 595 * 596 * @return The start position in the DN 597 */ 598 public int getStart() 599 { 600 return start; 601 } 602 603 604 /** 605 * Get the Rdn length 606 * 607 * @return The Rdn length 608 */ 609 public int getLength() 610 { 611 return length; 612 } 613 614 615 /** 616 * Get the AttributeTypeAndValue which type is given as an argument. If we 617 * have more than one value associated with the type, we will return only 618 * the first one. 619 * 620 * @param type 621 * The type of the NameArgument to be returned 622 * @return The AttributeTypeAndValue, of null if none is found. 623 */ 624 public AVA getAttributeTypeAndValue( String type ) 625 { 626 // First, let's normalize the type 627 String normalizedType = StringTools.lowerCaseAscii( StringTools.trim( type ) ); 628 629 switch ( nbAtavs ) 630 { 631 case 0: 632 return null; 633 634 case 1: 635 if ( atav.getNormType().equals( normalizedType ) ) 636 { 637 return atav; 638 } 639 640 return null; 641 642 default: 643 if ( atavTypes.containsKey( normalizedType ) ) 644 { 645 Collection<AVA> atavList = ( Collection<AVA> ) atavTypes.get( normalizedType ); 646 return atavList.iterator().next(); 647 } 648 649 return null; 650 } 651 } 652 653 654 /** 655 * Retrieves the components of this RDN as an iterator of AttributeTypeAndValue. 656 * The effect on the iterator of updates to this RDN is undefined. If the 657 * RDN has zero components, an empty (non-null) iterator is returned. 658 * 659 * @return an iterator of the components of this RDN, each an AttributeTypeAndValue 660 */ 661 public Iterator<AVA> iterator() 662 { 663 if ( nbAtavs == 1 || nbAtavs == 0 ) 664 { 665 return new Iterator<AVA>() 666 { 667 private boolean hasMoreElement = nbAtavs == 1; 668 669 670 public boolean hasNext() 671 { 672 return hasMoreElement; 673 } 674 675 676 public AVA next() 677 { 678 AVA obj = atav; 679 hasMoreElement = false; 680 return obj; 681 } 682 683 684 public void remove() 685 { 686 // nothing to do 687 } 688 }; 689 } 690 else 691 { 692 return atavs.iterator(); 693 } 694 } 695 696 697 /** 698 * Clone the Rdn 699 * 700 * @return A clone of the current RDN 701 */ 702 public Object clone() 703 { 704 try 705 { 706 RDN rdn = ( RDN ) super.clone(); 707 708 // The AttributeTypeAndValue is immutable. We won't clone it 709 710 switch ( rdn.getNbAtavs() ) 711 { 712 case 0: 713 break; 714 715 case 1: 716 rdn.atav = ( AVA ) this.atav.clone(); 717 rdn.atavTypes = atavTypes; 718 break; 719 720 default: 721 // We must duplicate the treeSet and the hashMap 722 rdn.atavTypes = new MultiValueMap(); 723 rdn.atavs = new TreeSet<AVA>(); 724 725 for ( AVA currentAtav : this.atavs ) 726 { 727 rdn.atavs.add( ( AVA ) currentAtav.clone() ); 728 rdn.atavTypes.put( currentAtav.getNormType(), currentAtav ); 729 } 730 731 break; 732 } 733 734 return rdn; 735 } 736 catch ( CloneNotSupportedException cnse ) 737 { 738 throw new Error( "Assertion failure" ); 739 } 740 } 741 742 743 /** 744 * Compares two RDNs. They are equals if : 745 * <li>their have the same number of NC (AttributeTypeAndValue) 746 * <li>each ATAVs are equals 747 * <li>comparison of type are done case insensitive 748 * <li>each value is equal, case sensitive 749 * <li>Order of ATAV is not important If the RDNs are not equals, a positive number is 750 * returned if the first RDN is greater, negative otherwise 751 * 752 * @param object 753 * @return 0 if both rdn are equals. -1 if the current RDN is inferior, 1 if 754 * the current Rdn is superior, UNDEFINED otherwise. 755 */ 756 public int compareTo( RDN rdn ) 757 { 758 if ( rdn == null ) 759 { 760 return SUPERIOR; 761 } 762 763 if ( rdn.nbAtavs != nbAtavs ) 764 { 765 // We don't have the same number of ATAVs. The Rdn which 766 // has the higher number of Atav is the one which is 767 // superior 768 return nbAtavs - rdn.nbAtavs; 769 } 770 771 switch ( nbAtavs ) 772 { 773 case 0: 774 return EQUAL; 775 776 case 1: 777 return atav.compareTo( rdn.atav ); 778 779 default: 780 // We have more than one value. We will 781 // go through all of them. 782 783 // the types are already normalized and sorted in the atavs TreeSet 784 // so we could compare the 1st with the 1st, then the 2nd with the 2nd, etc. 785 Iterator<AVA> localIterator = atavs.iterator(); 786 Iterator<AVA> paramIterator = rdn.atavs.iterator(); 787 788 while ( localIterator.hasNext() || paramIterator.hasNext() ) 789 { 790 if ( !localIterator.hasNext() ) 791 { 792 return SUPERIOR; 793 } 794 if ( !paramIterator.hasNext() ) 795 { 796 return INFERIOR; 797 } 798 799 AVA localAtav = localIterator.next(); 800 AVA paramAtav = paramIterator.next(); 801 int result = localAtav.compareTo( paramAtav ); 802 if ( result != EQUAL ) 803 { 804 return result; 805 } 806 } 807 808 return EQUAL; 809 } 810 } 811 812 813 /** 814 * @return the user provided name 815 */ 816 public String getName() 817 { 818 return upName; 819 } 820 821 822 /** 823 * @return The normalized name 824 */ 825 public String getNormName() 826 { 827 return normName == null ? "" : normName; 828 } 829 830 831 /** 832 * Set the User Provided Name 833 * @param upName the User Provided dame 834 */ 835 public void setUpName( String upName ) 836 { 837 this.upName = upName; 838 } 839 840 841 /** 842 * @return Returns the nbAtavs. 843 */ 844 public int getNbAtavs() 845 { 846 return nbAtavs; 847 } 848 849 850 /** 851 * Return the unique AttributeTypeAndValue, or the first one of we have more 852 * than one 853 * 854 * @return The first AttributeTypeAndValue of this RDN 855 */ 856 public AVA getAtav() 857 { 858 switch ( nbAtavs ) 859 { 860 case 0: 861 return null; 862 863 case 1: 864 return atav; 865 866 default: 867 return ( ( TreeSet<AVA> ) atavs ).first(); 868 } 869 } 870 871 872 /** 873 * Return the user provided type, or the first one of we have more than one (the lowest) 874 * 875 * @return The first user provided type of this RDN 876 */ 877 public String getUpType() 878 { 879 switch ( nbAtavs ) 880 { 881 case 0: 882 return null; 883 884 case 1: 885 return atav.getUpType(); 886 887 default: 888 return ( ( TreeSet<AVA> ) atavs ).first().getUpType(); 889 } 890 } 891 892 893 /** 894 * Return the normalized type, or the first one of we have more than one (the lowest) 895 * 896 * @return The first normalized type of this RDN 897 */ 898 public String getNormType() 899 { 900 switch ( nbAtavs ) 901 { 902 case 0: 903 return null; 904 905 case 1: 906 return atav.getNormType(); 907 908 default: 909 return ( ( TreeSet<AVA> ) atavs ).first().getNormType(); 910 } 911 } 912 913 914 /** 915 * Return the User Provided value 916 * 917 * @return The first User provided value of this RDN 918 */ 919 public String getUpValue() 920 { 921 switch ( nbAtavs ) 922 { 923 case 0: 924 return null; 925 926 case 1: 927 return atav.getUpValue().getString(); 928 929 default: 930 return ( ( TreeSet<AVA> ) atavs ).first().getUpValue().getString(); 931 } 932 } 933 934 935 /** 936 * Return the normalized value, or the first one of we have more than one (the lowest) 937 * 938 * @return The first normalized value of this RDN 939 */ 940 public String getNormValue() 941 { 942 switch ( nbAtavs ) 943 { 944 case 0: 945 return null; 946 947 case 1: 948 return atav.getNormValue().getString(); 949 950 default: 951 return ( ( TreeSet<AVA> ) atavs ).first().getNormValue().getString(); 952 } 953 } 954 955 956 /** 957 * Compares the specified Object with this Rdn for equality. Returns true if 958 * the given object is also a Rdn and the two Rdns represent the same 959 * attribute type and value mappings. The order of components in 960 * multi-valued Rdns is not significant. 961 * 962 * @param rdn 963 * Rdn to be compared for equality with this Rdn 964 * @return true if the specified object is equal to this Rdn 965 */ 966 public boolean equals( Object rdn ) 967 { 968 if ( this == rdn ) 969 { 970 return true; 971 } 972 973 if ( !( rdn instanceof RDN ) ) 974 { 975 return false; 976 } 977 978 return compareTo( (RDN)rdn ) == EQUAL; 979 } 980 981 982 /** 983 * Get the number of Attribute type and value of this Rdn 984 * 985 * @return The number of ATAVs in this Rdn 986 */ 987 public int size() 988 { 989 return nbAtavs; 990 } 991 992 993 /** 994 * Unescape the given string according to RFC 2253 If in <string> form, a 995 * LDAP string representation asserted value can be obtained by replacing 996 * (left-to-right, non-recursively) each <pair> appearing in the <string> as 997 * follows: replace <ESC><ESC> with <ESC>; replace <ESC><special> with 998 * <special>; replace <ESC><hexpair> with the octet indicated by the 999 * <hexpair> If in <hexstring> form, a BER representation can be obtained 1000 * from converting each <hexpair> of the <hexstring> to the octet indicated 1001 * by the <hexpair> 1002 * 1003 * @param value 1004 * The value to be unescaped 1005 * @return Returns a string value as a String, and a binary value as a byte 1006 * array. 1007 * @throws IllegalArgumentException - 1008 * When an Illegal value is provided. 1009 */ 1010 public static Object unescapeValue( String value ) 1011 { 1012 if ( StringTools.isEmpty( value ) ) 1013 { 1014 return ""; 1015 } 1016 1017 char[] chars = value.toCharArray(); 1018 1019 if ( chars[0] == '#' ) 1020 { 1021 if ( chars.length == 1 ) 1022 { 1023 // The value is only containing a # 1024 return StringTools.EMPTY_BYTES; 1025 } 1026 1027 if ( ( chars.length % 2 ) != 1 ) 1028 { 1029 throw new IllegalArgumentException( I18n.err( I18n.ERR_04213 ) ); 1030 } 1031 1032 // HexString form 1033 byte[] hexValue = new byte[( chars.length - 1 ) / 2]; 1034 int pos = 0; 1035 1036 for ( int i = 1; i < chars.length; i += 2 ) 1037 { 1038 if ( StringTools.isHex( chars, i ) && StringTools.isHex( chars, i + 1 ) ) 1039 { 1040 hexValue[pos++] = StringTools.getHexValue( chars[i], chars[i + 1] ); 1041 } 1042 else 1043 { 1044 throw new IllegalArgumentException( I18n.err( I18n.ERR_04214 ) ); 1045 } 1046 } 1047 1048 return hexValue; 1049 } 1050 else 1051 { 1052 boolean escaped = false; 1053 boolean isHex = false; 1054 byte pair = -1; 1055 int pos = 0; 1056 1057 byte[] bytes = new byte[chars.length * 6]; 1058 1059 for ( int i = 0; i < chars.length; i++ ) 1060 { 1061 if ( escaped ) 1062 { 1063 escaped = false; 1064 1065 switch ( chars[i] ) 1066 { 1067 case '\\': 1068 case '"': 1069 case '+': 1070 case ',': 1071 case ';': 1072 case '<': 1073 case '>': 1074 case '#': 1075 case '=': 1076 case ' ': 1077 bytes[pos++] = ( byte ) chars[i]; 1078 break; 1079 1080 default: 1081 if ( StringTools.isHex( chars, i ) ) 1082 { 1083 isHex = true; 1084 pair = ( ( byte ) ( StringTools.getHexValue( chars[i] ) << 4 ) ); 1085 } 1086 1087 break; 1088 } 1089 } 1090 else 1091 { 1092 if ( isHex ) 1093 { 1094 if ( StringTools.isHex( chars, i ) ) 1095 { 1096 pair += StringTools.getHexValue( chars[i] ); 1097 bytes[pos++] = pair; 1098 } 1099 } 1100 else 1101 { 1102 switch ( chars[i] ) 1103 { 1104 case '\\': 1105 escaped = true; 1106 break; 1107 1108 // We must not have a special char 1109 // Specials are : '"', '+', ',', ';', '<', '>', ' ', 1110 // '#' and '=' 1111 case '"': 1112 case '+': 1113 case ',': 1114 case ';': 1115 case '<': 1116 case '>': 1117 case '#': 1118 if ( i != 0 ) 1119 { 1120 // '#' are allowed if not in first position 1121 bytes[pos++] = '#'; 1122 break; 1123 } 1124 case '=': 1125 throw new IllegalArgumentException( I18n.err( I18n.ERR_04215 ) ); 1126 1127 case ' ': 1128 if ( ( i == 0 ) || ( i == chars.length - 1 ) ) 1129 { 1130 throw new IllegalArgumentException( I18n.err( I18n.ERR_04215 ) ); 1131 } 1132 else 1133 { 1134 bytes[pos++] = ' '; 1135 break; 1136 } 1137 1138 default: 1139 if ( ( chars[i] >= 0 ) && ( chars[i] < 128 ) ) 1140 { 1141 bytes[pos++] = ( byte ) chars[i]; 1142 } 1143 else 1144 { 1145 byte[] result = StringTools.charToBytes( chars[i] ); 1146 System.arraycopy( result, 0, bytes, pos, result.length ); 1147 pos += result.length; 1148 } 1149 1150 break; 1151 } 1152 } 1153 } 1154 } 1155 1156 return StringTools.utf8ToString( bytes, pos ); 1157 } 1158 } 1159 1160 1161 /** 1162 * Transform a value in a String, accordingly to RFC 2253 1163 * 1164 * @param value The attribute value to be escaped 1165 * @return The escaped string value. 1166 */ 1167 public static String escapeValue( String value ) 1168 { 1169 if ( StringTools.isEmpty( value ) ) 1170 { 1171 return ""; 1172 } 1173 1174 char[] chars = value.toCharArray(); 1175 char[] newChars = new char[chars.length * 3]; 1176 int pos = 0; 1177 1178 for ( int i = 0; i < chars.length; i++ ) 1179 { 1180 switch ( chars[i] ) 1181 { 1182 case ' ': 1183 if ( ( i > 0 ) && ( i < chars.length - 1 ) ) 1184 { 1185 newChars[pos++] = chars[i]; 1186 } 1187 else 1188 { 1189 newChars[pos++] = '\\'; 1190 newChars[pos++] = chars[i]; 1191 } 1192 1193 break; 1194 1195 case '#': 1196 if ( i != 0 ) 1197 { 1198 newChars[pos++] = chars[i]; 1199 } 1200 else 1201 { 1202 newChars[pos++] = '\\'; 1203 newChars[pos++] = chars[i]; 1204 } 1205 1206 break; 1207 1208 case '"': 1209 case '+': 1210 case ',': 1211 case ';': 1212 case '=': 1213 case '<': 1214 case '>': 1215 case '\\': 1216 newChars[pos++] = '\\'; 1217 newChars[pos++] = chars[i]; 1218 break; 1219 1220 case 0x7F: 1221 newChars[pos++] = '\\'; 1222 newChars[pos++] = '7'; 1223 newChars[pos++] = 'F'; 1224 break; 1225 1226 case 0x00: 1227 case 0x01: 1228 case 0x02: 1229 case 0x03: 1230 case 0x04: 1231 case 0x05: 1232 case 0x06: 1233 case 0x07: 1234 case 0x08: 1235 case 0x09: 1236 case 0x0A: 1237 case 0x0B: 1238 case 0x0C: 1239 case 0x0D: 1240 case 0x0E: 1241 case 0x0F: 1242 newChars[pos++] = '\\'; 1243 newChars[pos++] = '0'; 1244 newChars[pos++] = StringTools.dumpHex( ( byte ) ( chars[i] & 0x0F ) ); 1245 break; 1246 1247 case 0x10: 1248 case 0x11: 1249 case 0x12: 1250 case 0x13: 1251 case 0x14: 1252 case 0x15: 1253 case 0x16: 1254 case 0x17: 1255 case 0x18: 1256 case 0x19: 1257 case 0x1A: 1258 case 0x1B: 1259 case 0x1C: 1260 case 0x1D: 1261 case 0x1E: 1262 case 0x1F: 1263 newChars[pos++] = '\\'; 1264 newChars[pos++] = '1'; 1265 newChars[pos++] = StringTools.dumpHex( ( byte ) ( chars[i] & 0x0F ) ); 1266 break; 1267 1268 default: 1269 newChars[pos++] = chars[i]; 1270 break; 1271 1272 } 1273 } 1274 1275 return new String( newChars, 0, pos ); 1276 } 1277 1278 1279 /** 1280 * Transform a value in a String, accordingly to RFC 2253 1281 * 1282 * @param attrValue 1283 * The attribute value to be escaped 1284 * @return The escaped string value. 1285 */ 1286 public static String escapeValue( byte[] attrValue ) 1287 { 1288 if ( StringTools.isEmpty( attrValue ) ) 1289 { 1290 return ""; 1291 } 1292 1293 String value = StringTools.utf8ToString( attrValue ); 1294 1295 return escapeValue( value ); 1296 } 1297 1298 1299 /** 1300 * Gets the hashcode of this rdn. 1301 * 1302 * @see java.lang.Object#hashCode() 1303 * @return the instance's hash code 1304 */ 1305 public int hashCode() 1306 { 1307 int result = 37; 1308 1309 switch ( nbAtavs ) 1310 { 1311 case 0: 1312 // An empty RDN 1313 break; 1314 1315 case 1: 1316 // We have a single AttributeTypeAndValue 1317 result = result * 17 + atav.hashCode(); 1318 break; 1319 1320 default: 1321 // We have more than one AttributeTypeAndValue 1322 1323 for ( AVA ata : atavs ) 1324 { 1325 result = result * 17 + ata.hashCode(); 1326 } 1327 1328 break; 1329 } 1330 1331 return result; 1332 } 1333 1334 1335 /** 1336 * @see Externalizable#readExternal(ObjectInput)<p> 1337 * 1338 * A RDN is composed of on to many ATAVs (AttributeType And Value). 1339 * We should write all those ATAVs sequencially, following the 1340 * structure : 1341 * 1342 * <li>nbAtavs</li> The number of ATAVs to write. Can't be 0. 1343 * <li>upName</li> The User provided RDN 1344 * <li>normName</li> The normalized RDN. It can be empty if the normalized 1345 * name equals the upName. 1346 * <li>atavs</li> 1347 * <p> 1348 * For each ATAV :<p> 1349 * <li>start</li> The position of this ATAV in the upName string 1350 * <li>length</li> The ATAV user provided length 1351 * <li>Call the ATAV write method</li> The ATAV itself 1352 * 1353 * @param out The stream into which the serialized RDN will be put 1354 * @throws IOException If the stream can't be written 1355 */ 1356 public void writeExternal( ObjectOutput out ) throws IOException 1357 { 1358 out.writeInt( nbAtavs ); 1359 out.writeUTF( upName ); 1360 1361 if ( upName.equals( normName ) ) 1362 { 1363 out.writeUTF( "" ); 1364 } 1365 else 1366 { 1367 out.writeUTF( normName ); 1368 } 1369 1370 out.writeInt( start ); 1371 out.writeInt( length ); 1372 1373 switch ( nbAtavs ) 1374 { 1375 case 0: 1376 break; 1377 1378 case 1: 1379 out.writeObject( atav ); 1380 break; 1381 1382 default: 1383 for ( AVA value : atavs ) 1384 { 1385 out.writeObject( value ); 1386 } 1387 1388 break; 1389 } 1390 } 1391 1392 1393 /** 1394 * @see Externalizable#readExternal(ObjectInput) 1395 * 1396 * We read back the data to create a new RDB. The structure 1397 * read is exposed in the {@link RDN#writeExternal(ObjectOutput)} 1398 * method<p> 1399 * 1400 * @param in The input stream from which the RDN will be read 1401 * @throws IOException If we can't read from the input stream 1402 * @throws ClassNotFoundException If we can't create a new RDN 1403 */ 1404 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException 1405 { 1406 // Read the ATAV number 1407 nbAtavs = in.readInt(); 1408 1409 // Read the UPName 1410 upName = in.readUTF(); 1411 1412 // Read the normName 1413 normName = in.readUTF(); 1414 1415 if ( StringTools.isEmpty( normName ) ) 1416 { 1417 normName = upName; 1418 } 1419 1420 start = in.readInt(); 1421 length = in.readInt(); 1422 1423 switch ( nbAtavs ) 1424 { 1425 case 0: 1426 atav = null; 1427 break; 1428 1429 case 1: 1430 atav = ( AVA ) in.readObject(); 1431 atavType = atav.getNormType(); 1432 1433 break; 1434 1435 default: 1436 atavs = new TreeSet<AVA>(); 1437 1438 atavTypes = new MultiValueMap(); 1439 1440 for ( int i = 0; i < nbAtavs; i++ ) 1441 { 1442 AVA value = ( AVA ) in.readObject(); 1443 atavs.add( value ); 1444 atavTypes.put( value.getNormType(), value ); 1445 } 1446 1447 atav = null; 1448 atavType = null; 1449 1450 break; 1451 } 1452 } 1453 1454 1455 /** 1456 * @return a String representation of the RDN 1457 */ 1458 public String toString() 1459 { 1460 return upName == null ? "" : upName; 1461 } 1462 }