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.schema; 021 022 023 import java.util.HashSet; 024 import java.util.List; 025 import java.util.Map; 026 import java.util.Set; 027 import java.util.UUID; 028 029 import org.apache.directory.shared.i18n.I18n; 030 import org.apache.directory.shared.ldap.constants.MetaSchemaConstants; 031 import org.apache.directory.shared.ldap.entry.Entry; 032 import org.apache.directory.shared.ldap.entry.EntryAttribute; 033 import org.apache.directory.shared.ldap.entry.Modification; 034 import org.apache.directory.shared.ldap.entry.Value; 035 import org.apache.directory.shared.ldap.exception.LdapException; 036 import org.apache.directory.shared.ldap.util.StringTools; 037 038 039 /** 040 * Various utility methods for schema functions and objects. 041 * 042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 043 * @version $Rev: 928015 $ 044 */ 045 public class SchemaUtils 046 { 047 /** 048 * Gets the target entry as it would look after a modification operation 049 * were performed on it. 050 * 051 * @param mods the modifications performed on the entry 052 * @param entry the source entry that is modified 053 * @return the resultant entry after the modifications have taken place 054 * @throws LdapException if there are problems accessing attributes 055 */ 056 public static Entry getTargetEntry( List<? extends Modification> mods, Entry entry ) 057 throws LdapException 058 { 059 Entry targetEntry = entry.clone(); 060 061 for ( Modification mod : mods ) 062 { 063 String id = mod.getAttribute().getId(); 064 065 switch ( mod.getOperation() ) 066 { 067 case REPLACE_ATTRIBUTE : 068 targetEntry.put( mod.getAttribute() ); 069 break; 070 071 case ADD_ATTRIBUTE : 072 EntryAttribute combined = mod.getAttribute().clone(); 073 EntryAttribute toBeAdded = mod.getAttribute(); 074 EntryAttribute existing = entry.get( id ); 075 076 if ( existing != null ) 077 { 078 for ( Value<?> value:existing ) 079 { 080 combined.add( value ); 081 } 082 } 083 084 for ( Value<?> value:toBeAdded ) 085 { 086 combined.add( value ); 087 } 088 089 targetEntry.put( combined ); 090 break; 091 092 case REMOVE_ATTRIBUTE : 093 EntryAttribute toBeRemoved = mod.getAttribute(); 094 095 if ( toBeRemoved.size() == 0 ) 096 { 097 targetEntry.removeAttributes( id ); 098 } 099 else 100 { 101 existing = targetEntry.get( id ); 102 103 if ( existing != null ) 104 { 105 for ( Value<?> value:toBeRemoved ) 106 { 107 existing.remove( value ); 108 } 109 } 110 } 111 112 break; 113 114 default: 115 throw new IllegalStateException( I18n.err( I18n.ERR_04328, mod.getOperation() ) ); 116 } 117 } 118 119 return targetEntry; 120 } 121 122 123 // ------------------------------------------------------------------------ 124 // qdescrs rendering operations 125 // ------------------------------------------------------------------------ 126 127 /** 128 * Renders qdescrs into an existing buffer. 129 * 130 * @param buf 131 * the string buffer to render the quoted description strs into 132 * @param qdescrs 133 * the quoted description strings to render 134 * @return the same string buffer that was given for call chaining 135 */ 136 public static StringBuffer render( StringBuffer buf, List<String> qdescrs ) 137 { 138 if ( ( qdescrs == null ) || ( qdescrs.size() == 0 ) ) 139 { 140 return buf; 141 } 142 else if ( qdescrs.size() == 1 ) 143 { 144 buf.append( "'" ).append( qdescrs.get( 0 ) ).append( "'" ); 145 } 146 else 147 { 148 buf.append( "( " ); 149 150 for ( String qdescr : qdescrs ) 151 { 152 buf.append( "'" ).append( qdescr ).append( "' " ); 153 } 154 155 buf.append( ")" ); 156 } 157 158 return buf; 159 } 160 161 162 /** 163 * Renders qdescrs into a new buffer.<br> 164 * <pre> 165 * descrs ::= qdescr | '(' WSP qdescrlist WSP ')' 166 * qdescrlist ::= [ qdescr ( SP qdescr )* ] 167 * qdescr ::= SQUOTE descr SQUOTE 168 * </pre> 169 * @param qdescrs the quoted description strings to render 170 * @return the string buffer the qdescrs are rendered into 171 */ 172 /* No qualifier */ static StringBuffer renderQDescrs( StringBuffer buf, List<String> qdescrs ) 173 { 174 if ( ( qdescrs == null ) || ( qdescrs.size() == 0 ) ) 175 { 176 return buf; 177 } 178 179 if ( qdescrs.size() == 1 ) 180 { 181 buf.append( '\'' ).append( qdescrs.get( 0 ) ).append( '\'' ); 182 } 183 else 184 { 185 buf.append( "( " ); 186 187 for ( String qdescr : qdescrs ) 188 { 189 buf.append( '\'' ).append( qdescr ).append( "' " ); 190 } 191 192 buf.append( ")" ); 193 } 194 195 return buf; 196 } 197 198 199 /** 200 * Renders oids into a new buffer.<br> 201 * <pre> 202 * oids ::= oid | '(' WSP oidlist WSP ')' 203 * oidlist ::= oid ( WSP '$' WSP oid )* 204 * </pre> 205 * 206 * @param qdescrs the quoted description strings to render 207 * @return the string buffer the qdescrs are rendered into 208 */ 209 private static StringBuffer renderOids( StringBuffer buf, List<String> oids ) 210 { 211 if ( oids.size() == 1 ) 212 { 213 buf.append( oids.get( 0 ) ); 214 } 215 else 216 { 217 buf.append( "( " ); 218 219 boolean isFirst = true; 220 221 for ( String oid : oids ) 222 { 223 if ( isFirst ) 224 { 225 isFirst = false; 226 } 227 else 228 { 229 buf.append( " $ " ); 230 } 231 232 buf.append( oid ); 233 } 234 235 buf.append( " )" ); 236 } 237 238 return buf; 239 } 240 241 242 /** 243 * Renders QDString into a new buffer.<br> 244 * <pre> 245 * 246 * @param qdescrs the quoted description strings to render 247 * @return the string buffer the qdescrs are rendered into 248 */ 249 private static StringBuffer renderQDString( StringBuffer buf, String qdString ) 250 { 251 buf.append( '\'' ); 252 253 for ( char c : qdString.toCharArray() ) 254 { 255 switch ( c ) 256 { 257 case 0x27 : 258 buf.append( "\\27" ); 259 break; 260 261 case 0x5C : 262 buf.append( "\\5C" ); 263 break; 264 265 default : 266 buf.append( c ); 267 break; 268 } 269 } 270 271 buf.append( '\'' ); 272 273 return buf; 274 } 275 276 // ------------------------------------------------------------------------ 277 // objectClass list rendering operations 278 // ------------------------------------------------------------------------ 279 280 /** 281 * Renders a list of object classes for things like a list of superior 282 * objectClasses using the ( oid $ oid ) format. 283 * 284 * @param ocs 285 * the objectClasses to list 286 * @return a buffer which contains the rendered list 287 */ 288 public static StringBuffer render( ObjectClass[] ocs ) 289 { 290 StringBuffer buf = new StringBuffer(); 291 292 return render( buf, ocs ); 293 } 294 295 296 /** 297 * Renders a list of object classes for things like a list of superior 298 * objectClasses using the ( oid $ oid ) format into an existing buffer. 299 * 300 * @param buf 301 * the string buffer to render the list of objectClasses into 302 * @param ocs 303 * the objectClasses to list 304 * @return a buffer which contains the rendered list 305 */ 306 public static StringBuffer render( StringBuffer buf, ObjectClass[] ocs ) 307 { 308 if ( ocs == null || ocs.length == 0 ) 309 { 310 return buf; 311 } 312 else if ( ocs.length == 1 ) 313 { 314 buf.append( ocs[0].getName() ); 315 } 316 else 317 { 318 buf.append( "( " ); 319 320 for ( int ii = 0; ii < ocs.length; ii++ ) 321 { 322 if ( ii + 1 < ocs.length ) 323 { 324 buf.append( ocs[ii].getName() ).append( " $ " ); 325 } 326 else 327 { 328 buf.append( ocs[ii].getName() ); 329 } 330 } 331 332 buf.append( " )" ); 333 } 334 335 return buf; 336 } 337 338 339 // ------------------------------------------------------------------------ 340 // attributeType list rendering operations 341 // ------------------------------------------------------------------------ 342 343 /** 344 * Renders a list of attributeTypes for things like the must or may list of 345 * objectClasses using the ( oid $ oid ) format. 346 * 347 * @param ats 348 * the attributeTypes to list 349 * @return a buffer which contains the rendered list 350 */ 351 public static StringBuffer render( AttributeType[] ats ) 352 { 353 StringBuffer buf = new StringBuffer(); 354 return render( buf, ats ); 355 } 356 357 358 /** 359 * Renders a list of attributeTypes for things like the must or may list of 360 * objectClasses using the ( oid $ oid ) format into an existing buffer. 361 * 362 * @param buf 363 * the string buffer to render the list of attributeTypes into 364 * @param ats 365 * the attributeTypes to list 366 * @return a buffer which contains the rendered list 367 */ 368 public static StringBuffer render( StringBuffer buf, AttributeType[] ats ) 369 { 370 if ( ats == null || ats.length == 0 ) 371 { 372 return buf; 373 } 374 else if ( ats.length == 1 ) 375 { 376 buf.append( ats[0].getName() ); 377 } 378 else 379 { 380 buf.append( "( " ); 381 for ( int ii = 0; ii < ats.length; ii++ ) 382 { 383 if ( ii + 1 < ats.length ) 384 { 385 buf.append( ats[ii].getName() ).append( " $ " ); 386 } 387 else 388 { 389 buf.append( ats[ii].getName() ); 390 } 391 } 392 buf.append( " )" ); 393 } 394 395 return buf; 396 } 397 398 399 // ------------------------------------------------------------------------ 400 // schema object rendering operations 401 // ------------------------------------------------------------------------ 402 403 /** 404 * Renders an objectClass into a new StringBuffer according to the Object 405 * Class Description Syntax 1.3.6.1.4.1.1466.115.121.1.37. The syntax is 406 * described in detail within section 4.1.1. of LDAPBIS [<a 407 * href="http://ietf.org/internet-drafts/draft-ietf-ldapbis-models-12.txt">MODELS</a>] 408 * which is replicated here for convenience: 409 * 410 * <pre> 411 * 4.1.1. Object Class Definitions 412 * 413 * Object Class definitions are written according to the ABNF: 414 * 415 * ObjectClassDescription = LPAREN WSP 416 * numericoid ; object identifier 417 * [ SP "NAME" SP qdescrs ] ; short names (descriptors) 418 * [ SP "DESC" SP qdstring ] ; description 419 * [ SP "OBSOLETE" ] ; not active 420 * [ SP "SUP" SP oids ] ; superior object classes 421 * [ SP kind ] ; kind of class 422 * [ SP "MUST" SP oids ] ; attribute types 423 * [ SP "MAY" SP oids ] ; attribute types 424 * extensions WSP RPAREN 425 * 426 * kind = "ABSTRACT" / "STRUCTURAL" / "AUXILIARY" 427 * 428 * where: 429 * <numericoid> is object identifier assigned to this object class; 430 * NAME <qdescrs> are short names (descriptors) identifying this object 431 * class; 432 * DESC <qdstring> is a short descriptive string; 433 * OBSOLETE indicates this object class is not active; 434 * SUP <oids> specifies the direct superclasses of this object class; 435 * the kind of object class is indicated by one of ABSTRACT, 436 * STRUCTURAL, or AUXILIARY, default is STRUCTURAL; 437 * MUST and MAY specify the sets of required and allowed attribute 438 * types, respectively; and 439 * <extensions> describe extensions. 440 * </pre> 441 * @param oc the objectClass to render the description of 442 * @return the buffer containing the objectClass description 443 * @throws LdapException if there are any problems accessing objectClass 444 * information 445 */ 446 public static StringBuffer render( ObjectClass oc ) throws LdapException 447 { 448 StringBuffer buf = new StringBuffer(); 449 buf.append( "( " ).append( oc.getOid() ); 450 451 List<String> names = oc.getNames(); 452 453 if ( ( names != null ) && ( names.size() > 0 ) ) 454 { 455 buf.append( " NAME " ); 456 renderQDescrs( buf, names ); 457 } 458 459 if ( oc.getDescription() != null ) 460 { 461 buf.append( " DESC " ); 462 renderQDString( buf, oc.getDescription() ); 463 } 464 465 if ( oc.isObsolete() ) 466 { 467 buf.append( " OBSOLETE" ); 468 } 469 470 List<String> superiorOids = oc.getSuperiorOids(); 471 472 if ( ( superiorOids != null ) && ( superiorOids.size() > 0 ) ) 473 { 474 buf.append( " SUP " ); 475 renderOids( buf, superiorOids ); 476 } 477 478 if ( oc.getType() != null ) 479 { 480 buf.append( " " ).append( oc.getType() ); 481 } 482 483 List<String> must = oc.getMustAttributeTypeOids(); 484 485 if ( ( must != null ) && ( must.size() > 0 ) ) 486 { 487 buf.append( " MUST " ); 488 renderOids( buf, must ); 489 } 490 491 List<String> may = oc.getMayAttributeTypeOids(); 492 493 if ( ( may != null ) && ( may.size() > 0 ) ) 494 { 495 buf.append( " MAY " ); 496 renderOids( buf, may ); 497 } 498 499 buf.append( " X-SCHEMA '" ); 500 buf.append( oc.getSchemaName() ); 501 buf.append( "'" ); 502 503 // @todo extensions are not presently supported and skipped 504 // the extensions would go here before closing off the description 505 506 buf.append( " )" ); 507 508 return buf; 509 } 510 511 512 /** 513 * Renders an attributeType into a new StringBuffer according to the 514 * Attribute Type Description Syntax 1.3.6.1.4.1.1466.115.121.1.3. The 515 * syntax is described in detail within section 4.1.2. of LDAPBIS [<a 516 * href="http://ietf.org/internet-drafts/draft-ietf-ldapbis-models-12.txt">MODELS</a>] 517 * which is replicated here for convenience: 518 * 519 * <pre> 520 * 4.1.2. Attribute Types 521 * 522 * Attribute Type definitions are written according to the ABNF: 523 * 524 * AttributeTypeDescription = LPAREN WSP 525 * numericoid ; object identifier 526 * [ SP "NAME" SP qdescrs ] ; short names (descriptors) 527 * [ SP "DESC" SP qdstring ] ; description 528 * [ SP "OBSOLETE" ] ; not active 529 * [ SP "SUP" SP oid ] ; supertype 530 * [ SP "EQUALITY" SP oid ] ; equality matching rule 531 * [ SP "ORDERING" SP oid ] ; ordering matching rule 532 * [ SP "SUBSTR" SP oid ] ; substrings matching rule 533 * [ SP "SYNTAX" SP noidlen ] ; value syntax 534 * [ SP "SINGLE-VALUE" ] ; single-value 535 * [ SP "COLLECTIVE" ] ; collective 536 * [ SP "NO-USER-MODIFICATION" ] ; not user modifiable 537 * [ SP "USAGE" SP usage ] ; usage 538 * extensions WSP RPAREN ; extensions 539 * 540 * usage = "userApplications" / ; user 541 * "directoryOperation" / ; directory operational 542 * "distributedOperation" / ; DSA-shared operational 543 * "dSAOperation" ; DSA-specific operational 544 * 545 * where: 546 * <numericoid> is object identifier assigned to this attribute type; 547 * NAME <qdescrs> are short names (descriptors) identifying this 548 * attribute type; 549 * DESC <qdstring> is a short descriptive string; 550 * OBSOLETE indicates this attribute type is not active; 551 * SUP oid specifies the direct supertype of this type; 552 * EQUALITY, ORDERING, SUBSTR provide the oid of the equality, 553 * ordering, and substrings matching rules, respectively; 554 * SYNTAX identifies value syntax by object identifier and may suggest 555 * a minimum upper bound; 556 * SINGLE-VALUE indicates attributes of this type are restricted to a 557 * single value; 558 * COLLECTIVE indicates this attribute type is collective 559 * [X.501][RFC3671]; 560 * NO-USER-MODIFICATION indicates this attribute type is not user 561 * modifiable; 562 * USAGE indicates the application of this attribute type; and 563 * <extensions> describe extensions. 564 * </pre> 565 * @param at the AttributeType to render the description for 566 * @return the StringBuffer containing the rendered attributeType description 567 * @throws LdapException if there are problems accessing the objects 568 * associated with the attribute type. 569 */ 570 public static StringBuffer render( AttributeType at ) throws LdapException 571 { 572 StringBuffer buf = new StringBuffer(); 573 buf.append( "( " ).append( at.getOid() ); 574 List<String> names = at.getNames(); 575 576 if ( ( names != null ) && ( names.size() > 0 ) ) 577 { 578 buf.append( " NAME " ); 579 renderQDescrs( buf, names ); 580 } 581 582 if ( at.getDescription() != null ) 583 { 584 buf.append( " DESC " ); 585 renderQDString( buf, at.getDescription() ); 586 } 587 588 if ( at.isObsolete() ) 589 { 590 buf.append( " OBSOLETE" ); 591 } 592 593 if ( at.getSuperior() != null ) 594 { 595 buf.append( " SUP " ).append( at.getSuperior().getName() ); 596 } 597 598 if ( at.getEquality() != null ) 599 { 600 buf.append( " EQUALITY " ).append( at.getEquality().getName() ); 601 } 602 603 if ( at.getOrdering() != null ) 604 { 605 buf.append( " ORDERING " ).append( at.getOrdering().getName() ); 606 } 607 608 if ( at.getSubstring() != null ) 609 { 610 buf.append( " SUBSTR " ).append( at.getSubstring().getName() ); 611 } 612 613 if ( at.getSyntax() != null ) 614 { 615 buf.append( " SYNTAX " ).append( at.getSyntax().getOid() ); 616 617 if ( at.getSyntaxLength() > 0 ) 618 { 619 buf.append( "{" ).append( at.getSyntaxLength() ).append( "}" ); 620 } 621 } 622 623 if ( at.isSingleValued() ) 624 { 625 buf.append( " SINGLE-VALUE" ); 626 } 627 628 if ( at.isCollective() ) 629 { 630 buf.append( " COLLECTIVE" ); 631 } 632 633 if ( !at.isUserModifiable() ) 634 { 635 buf.append( " NO-USER-MODIFICATION" ); 636 } 637 638 if ( at.getUsage() != null ) 639 { 640 buf.append( " USAGE " ).append( UsageEnum.render( at.getUsage() ) ); 641 } 642 643 buf.append( " X-SCHEMA '" ); 644 buf.append( at.getSchemaName() ); 645 buf.append( "'" ); 646 647 // @todo extensions are not presently supported and skipped 648 // the extensions would go here before closing off the description 649 650 buf.append( " )" ); 651 652 return buf; 653 } 654 655 656 /** 657 * Renders an attributeType description object into a new StringBuffer 658 * according to the Attribute Type Description Syntax defined in MODELS 659 * 1.3.6.1.4.1.1466.115.121.1.3. The syntax is described in detail within 660 * section 4.1.2. of (@TODO NEEDS TO CHANGE SINCE THIS IS NOW AN RFC) LDAPBIS [<a 661 * href="http://ietf.org/internet-drafts/draft-ietf-ldapbis-models-12.txt">MODELS</a>] 662 * which is replicated here for convenience: 663 * 664 * <pre> 665 * 4.1.2. Attribute Types 666 * 667 * Attribute Type definitions are written according to the ABNF: 668 * 669 * AttributeTypeDescription = LPAREN WSP 670 * numericoid ; object identifier 671 * [ SP "NAME" SP qdescrs ] ; short names (descriptors) 672 * [ SP "DESC" SP qdstring ] ; description 673 * [ SP "OBSOLETE" ] ; not active 674 * [ SP "SUP" SP oid ] ; supertype 675 * [ SP "EQUALITY" SP oid ] ; equality matching rule 676 * [ SP "ORDERING" SP oid ] ; ordering matching rule 677 * [ SP "SUBSTR" SP oid ] ; substrings matching rule 678 * [ SP "SYNTAX" SP noidlen ] ; value syntax 679 * [ SP "SINGLE-VALUE" ] ; single-value 680 * [ SP "COLLECTIVE" ] ; collective 681 * [ SP "NO-USER-MODIFICATION" ] ; not user modifiable 682 * [ SP "USAGE" SP usage ] ; usage 683 * extensions WSP RPAREN ; extensions 684 * 685 * usage = "userApplications" / ; user 686 * "directoryOperation" / ; directory operational 687 * "distributedOperation" / ; DSA-shared operational 688 * "dSAOperation" ; DSA-specific operational 689 * 690 * where: 691 * <numericoid> is object identifier assigned to this attribute type; 692 * NAME <qdescrs> are short names (descriptors) identifying this 693 * attribute type; 694 * DESC <qdstring> is a short descriptive string; 695 * OBSOLETE indicates this attribute type is not active; 696 * SUP oid specifies the direct supertype of this type; 697 * EQUALITY, ORDERING, SUBSTR provide the oid of the equality, 698 * ordering, and substrings matching rules, respectively; 699 * SYNTAX identifies value syntax by object identifier and may suggest 700 * a minimum upper bound; 701 * SINGLE-VALUE indicates attributes of this type are restricted to a 702 * single value; 703 * COLLECTIVE indicates this attribute type is collective 704 * [X.501][RFC3671]; 705 * NO-USER-MODIFICATION indicates this attribute type is not user 706 * modifiable; 707 * USAGE indicates the application of this attribute type; and 708 * <extensions> describe extensions. 709 * </pre> 710 * @param atd the AttributeTypeDescription to render the description for 711 * @return the StringBuffer containing the rendered attributeType description 712 * @throws LdapException if there are problems accessing the objects 713 * associated with the attribute type. 714 * 715 public static StringBuffer render( AttributeType attributeType ) 716 { 717 StringBuffer buf = new StringBuffer(); 718 buf.append( "( " ).append( attributeType.getOid() ); 719 720 if ( attributeType.getNames() != null && attributeType.getNames().size() > 0 ) 721 { 722 buf.append( " NAME " ); 723 render( buf, attributeType.getNames() ).append( " " ); 724 } 725 else 726 { 727 buf.append( " " ); 728 } 729 730 if ( attributeType.getDescription() != null ) 731 { 732 buf.append( "DESC " ).append( "'" ).append( attributeType.getDescription() ).append( "' " ); 733 } 734 735 if ( attributeType.isObsolete() ) 736 { 737 buf.append( " OBSOLETE" ); 738 } 739 740 if ( attributeType.getSupOid() != null ) 741 { 742 buf.append( " SUP " ).append( attributeType.getSupOid() ); 743 } 744 745 if ( attributeType.getEqualityOid() != null ) 746 { 747 buf.append( " EQUALITY " ).append( attributeType.getEqualityOid() ); 748 } 749 750 if ( attributeType.getOrderingOid() != null ) 751 { 752 buf.append( " ORDERING " ).append( attributeType.getOrderingOid() ); 753 } 754 755 if ( attributeType.getSubstrOid() != null ) 756 { 757 buf.append( " SUBSTR " ).append( attributeType.getSubstrOid() ); 758 } 759 760 if ( attributeType.getSyntax() != null ) 761 { 762 buf.append( " SYNTAX " ).append( attributeType.getSyntax() ); 763 764 if ( attributeType.getLength() > 0 ) 765 { 766 buf.append( "{" ).append( attributeType.getLength() ).append( "}" ); 767 } 768 } 769 770 if ( attributeType.isSingleValue() ) 771 { 772 buf.append( " SINGLE-VALUE" ); 773 } 774 775 if ( attributeType.isCollective() ) 776 { 777 buf.append( " COLLECTIVE" ); 778 } 779 780 if ( !attributeType.isCanUserModify() ) 781 { 782 buf.append( " NO-USER-MODIFICATION" ); 783 } 784 785 if ( attributeType.getUsage() != null ) 786 { 787 buf.append( " USAGE " ).append( UsageEnum.render( attributeType.getUsage() ) ); 788 } 789 790 return buf.append( render( attributeType.getExtensions() ) ).append( ")" ); 791 } 792 793 794 /** 795 * Renders the schema extensions into a new StringBuffer. 796 * 797 * @param extensions the schema extensions map with key and values 798 * @return a StringBuffer with the extensions component of a syntax description 799 */ 800 public static StringBuffer render( Map<String, List<String>> extensions ) 801 { 802 StringBuffer buf = new StringBuffer(); 803 804 if ( extensions.isEmpty() ) 805 { 806 return buf; 807 } 808 809 for ( String key : extensions.keySet() ) 810 { 811 buf.append( " " ).append( key ).append( " " ); 812 813 List<String> values = extensions.get( key ); 814 815 // For extensions without values like X-IS-HUMAN-READIBLE 816 if ( values == null || values.isEmpty() ) 817 { 818 continue; 819 } 820 821 // For extensions with a single value we can use one qdstring like 'value' 822 if ( values.size() == 1 ) 823 { 824 buf.append( "'" ).append( values.get( 0 ) ).append( "' " ); 825 continue; 826 } 827 828 // For extensions with several values we have to surround whitespace 829 // separated list of qdstrings like ( 'value0' 'value1' 'value2' ) 830 buf.append( "( " ); 831 for ( String value : values ) 832 { 833 buf.append( "'" ).append( value ).append( "' " ); 834 } 835 buf.append( ")" ); 836 } 837 838 if ( buf.charAt( buf.length() - 1 ) != ' ' ) 839 { 840 buf.append( " " ); 841 } 842 843 return buf; 844 } 845 846 847 /** 848 * Renders an matchingRule into a new StringBuffer according to the 849 * MatchingRule Description Syntax 1.3.6.1.4.1.1466.115.121.1.30. The syntax 850 * is described in detail within section 4.1.3. of LDAPBIS [<a 851 * href="http://ietf.org/internet-drafts/draft-ietf-ldapbis-models-12.txt">MODELS</a>] 852 * which is replicated here for convenience: 853 * 854 * <pre> 855 * 4.1.3. Matching Rules 856 * 857 * Matching rules are used in performance of attribute value assertions, 858 * such as in performance of a Compare operation. They are also used in 859 * evaluation of a Search filters, in determining which individual values 860 * are be added or deleted during performance of a Modify operation, and 861 * used in comparison of distinguished names. 862 * 863 * Each matching rule is identified by an object identifier (OID) and, 864 * optionally, one or more short names (descriptors). 865 * 866 * Matching rule definitions are written according to the ABNF: 867 * 868 * MatchingRuleDescription = LPAREN WSP 869 * numericoid ; object identifier 870 * [ SP "NAME" SP qdescrs ] ; short names (descriptors) 871 * [ SP "DESC" SP qdstring ] ; description 872 * [ SP "OBSOLETE" ] ; not active 873 * SP "SYNTAX" SP numericoid ; assertion syntax 874 * extensions WSP RPAREN ; extensions 875 * 876 * where: 877 * <numericoid> is object identifier assigned to this matching rule; 878 * NAME <qdescrs> are short names (descriptors) identifying this 879 * matching rule; 880 * DESC <qdstring> is a short descriptive string; 881 * OBSOLETE indicates this matching rule is not active; 882 * SYNTAX identifies the assertion syntax (the syntax of the assertion 883 * value) by object identifier; and 884 * <extensions> describe extensions. 885 * </pre> 886 * @param mr the MatchingRule to render the description for 887 * @return the StringBuffer containing the rendered matchingRule description 888 * @throws LdapException if there are problems accessing the objects 889 * associated with the MatchingRule. 890 */ 891 public static StringBuffer render( MatchingRule mr ) throws LdapException 892 { 893 StringBuffer buf = new StringBuffer(); 894 buf.append( "( " ).append( mr.getOid() ); 895 896 List<String> names = mr.getNames(); 897 898 if ( ( names != null ) && ( names.size() > 0 ) ) 899 { 900 buf.append( " NAME " ); 901 renderQDescrs( buf, names ); 902 } 903 904 if ( mr.getDescription() != null ) 905 { 906 buf.append( " DESC " ); 907 renderQDString( buf, mr.getDescription() ); 908 } 909 910 if ( mr.isObsolete() ) 911 { 912 buf.append( " OBSOLETE" ); 913 } 914 915 buf.append( " SYNTAX " ).append( mr.getSyntax().getOid() ); 916 917 buf.append( " X-SCHEMA '" ); 918 buf.append( mr.getSchemaName() ); 919 buf.append( "'" ); 920 921 // @todo extensions are not presently supported and skipped 922 // the extensions would go here before closing off the description 923 924 buf.append( " )" ); 925 926 return buf; 927 } 928 929 930 /** 931 * Renders a Syntax into a new StringBuffer according to the LDAP Syntax 932 * Description Syntax 1.3.6.1.4.1.1466.115.121.1.54. The syntax is described 933 * in detail within section 4.1.5. of LDAPBIS [<a 934 * href="http://ietf.org/internet-drafts/draft-ietf-ldapbis-models-12.txt">MODELS</a>] 935 * which is replicated here for convenience: 936 * 937 * <pre> 938 * LDAP syntax definitions are written according to the ABNF: 939 * 940 * SyntaxDescription = LPAREN WSP 941 * numericoid ; object identifier 942 * [ SP "DESC" SP qdstring ] ; description 943 * extensions WSP RPAREN ; extensions 944 * 945 * where: 946 * <numericoid> is the object identifier assigned to this LDAP syntax; 947 * DESC <qdstring> is a short descriptive string; and 948 * <extensions> describe extensions. 949 * </pre> 950 * @param syntax the Syntax to render the description for 951 * @return the StringBuffer containing the rendered syntax description 952 */ 953 public static StringBuffer render( LdapSyntax syntax ) 954 { 955 StringBuffer buf = new StringBuffer(); 956 buf.append( "( " ).append( syntax.getOid() ); 957 958 if ( syntax.getDescription() != null ) 959 { 960 buf.append( " DESC " ); 961 renderQDString( buf, syntax.getDescription() ); 962 } 963 964 buf.append( " X-SCHEMA '" ); 965 buf.append( syntax.getSchemaName() ); 966 967 if ( syntax.isHumanReadable() ) 968 { 969 buf.append( "' X-IS-HUMAN-READABLE 'true'" ); 970 } 971 else 972 { 973 buf.append( "' X-IS-HUMAN-READABLE 'false'" ); 974 } 975 976 // @todo extensions are not presently supported and skipped 977 // the extensions would go here before closing off the description 978 979 buf.append( " )" ); 980 981 return buf; 982 } 983 984 985 /** 986 * NOT FULLY IMPLEMENTED! 987 */ 988 public static StringBuffer render( MatchingRuleUse mru ) 989 { 990 StringBuffer buf = new StringBuffer(); 991 buf.append( "( " ).append( mru.getOid() ); 992 993 List<String> names = mru.getNames(); 994 995 if ( ( names != null ) && ( names.size() > 0 ) ) 996 { 997 buf.append( " NAME " ); 998 renderQDescrs( buf, names ); 999 } 1000 1001 if ( mru.getDescription() != null ) 1002 { 1003 buf.append( " DESC " ); 1004 renderQDString( buf, mru.getDescription() ); 1005 } 1006 1007 if ( mru.isObsolete ) 1008 { 1009 buf.append( " OBSOLETE" ); 1010 } 1011 1012 List<String> applies = mru.getApplicableAttributeOids(); 1013 1014 if ( ( applies != null ) && ( applies.size() > 0 ) ) 1015 { 1016 buf.append( " APPLIES " ); 1017 renderOids( buf, applies ); 1018 } 1019 1020 buf.append( " X-SCHEMA '" ); 1021 buf.append( mru.getSchemaName() ); 1022 buf.append( "'" ); 1023 1024 // @todo extensions are not presently supported and skipped 1025 // the extensions would go here before closing off the description 1026 1027 buf.append( " )" ); 1028 1029 return buf; 1030 } 1031 1032 1033 /** 1034 * NOT FULLY IMPLEMENTED! 1035 */ 1036 public static StringBuffer render( DITContentRule dcr ) 1037 { 1038 StringBuffer buf = new StringBuffer(); 1039 buf.append( "( " ).append( dcr.getOid() ); 1040 1041 List<String> names = dcr.getNames(); 1042 1043 if ( ( names != null ) && ( names.size() > 0 ) ) 1044 { 1045 buf.append( " NAME " ); 1046 renderQDescrs( buf, names ); 1047 } 1048 1049 if ( dcr.getDescription() != null ) 1050 { 1051 buf.append( " DESC " ); 1052 renderQDString( buf, dcr.getDescription() ); 1053 } 1054 1055 if ( dcr.isObsolete ) 1056 { 1057 buf.append( " OBSOLETE" ); 1058 } 1059 1060 List<String> aux = dcr.getAuxObjectClassOids(); 1061 1062 if ( ( aux != null ) && ( aux.size() > 0 ) ) 1063 { 1064 buf.append( " AUX " ); 1065 renderOids( buf, aux ); 1066 } 1067 1068 List<String> must = dcr.getMustAttributeTypeOids(); 1069 1070 if ( ( must != null ) && ( must.size() > 0 ) ) 1071 { 1072 buf.append( " MUST " ); 1073 renderOids( buf, must ); 1074 } 1075 1076 List<String> may = dcr.getMayAttributeTypeOids(); 1077 1078 if ( ( may != null ) && ( may.size() > 0 ) ) 1079 { 1080 buf.append( " MAY " ); 1081 renderOids( buf, may ); 1082 } 1083 1084 List<String> not = dcr.getNotAttributeTypeOids(); 1085 1086 if ( ( not != null ) && ( not.size() > 0 ) ) 1087 { 1088 buf.append( " AUX " ); 1089 renderOids( buf, not ); 1090 } 1091 1092 buf.append( " X-SCHEMA '" ); 1093 buf.append( dcr.getSchemaName() ); 1094 buf.append( "'" ); 1095 1096 // @todo extensions are not presently supported and skipped 1097 // the extensions would go here before closing off the description 1098 1099 buf.append( " )" ); 1100 1101 return buf; 1102 } 1103 1104 1105 /** 1106 * NOT FULLY IMPLEMENTED! 1107 */ 1108 public static StringBuffer render( DITStructureRule dsr ) 1109 { 1110 StringBuffer buf = new StringBuffer(); 1111 buf.append( "( " ).append( dsr.getOid() ); 1112 1113 List<String> names = dsr.getNames(); 1114 1115 if ( ( names != null ) && ( names.size() > 0 ) ) 1116 { 1117 buf.append( " NAME " ); 1118 renderQDescrs( buf, names ); 1119 } 1120 1121 if ( dsr.getDescription() != null ) 1122 { 1123 buf.append( " DESC " ); 1124 renderQDString( buf, dsr.getDescription() ); 1125 } 1126 1127 if ( dsr.isObsolete ) 1128 { 1129 buf.append( " OBSOLETE" ); 1130 } 1131 1132 buf.append( " FORM " ).append( dsr.getForm() ); 1133 1134 List<Integer> ruleIds = dsr.getSuperRules(); 1135 // TODO : Add the rendering 1136 1137 buf.append( " X-SCHEMA '" ); 1138 buf.append( dsr.getSchemaName() ); 1139 buf.append( "' )" ); 1140 1141 return buf; 1142 } 1143 1144 1145 /** 1146 * NOT FULLY IMPLEMENTED! 1147 */ 1148 public static StringBuffer render( NameForm nf ) 1149 { 1150 StringBuffer buf = new StringBuffer(); 1151 buf.append( "( " ).append( nf.getOid() ); 1152 1153 List<String> names = nf.getNames(); 1154 1155 if ( ( names != null ) && ( names.size() > 0 ) ) 1156 { 1157 buf.append( " NAME " ); 1158 renderQDescrs( buf, names ); 1159 } 1160 1161 if ( nf.getDescription() != null ) 1162 { 1163 buf.append( " DESC " ); 1164 renderQDString( buf, nf.getDescription() ); 1165 } 1166 1167 if ( nf.isObsolete ) 1168 { 1169 buf.append( " OBSOLETE" ); 1170 } 1171 1172 buf.append( " OC " ); 1173 buf.append( nf.getStructuralObjectClass().getName() ); 1174 1175 buf.append( " MUST " ); 1176 renderOids( buf, nf.getMustAttributeTypeOids() ); 1177 1178 List<String> may = nf.getMayAttributeTypeOids(); 1179 1180 if ( ( may != null ) && ( may.size() > 0 ) ) 1181 { 1182 buf.append( " MAY " ); 1183 renderOids( buf, may ); 1184 } 1185 1186 buf.append( " X-SCHEMA '" ); 1187 buf.append( nf.getSchemaName() ); 1188 buf.append( "' )" ); 1189 1190 return buf; 1191 } 1192 1193 1194 /** 1195 * Returns a String description of a schema. The resulting String format is : 1196 * <br> 1197 * (OID [DESC '<description>'] FQCN <fcqn> [BYTECODE <bytecode>] X-SCHEMA '<schema>') 1198 * <br> 1199 * @param description The description to transform to a String 1200 * @return 1201 */ 1202 public static String render( LoadableSchemaObject description ) 1203 { 1204 StringBuffer buf = new StringBuffer(); 1205 buf.append( "( " ).append( description.getOid() ); 1206 1207 if ( description.getDescription() != null ) 1208 { 1209 buf.append( " DESC " ); 1210 renderQDString( buf, description.getDescription() ); 1211 } 1212 1213 buf.append( " FQCN " ).append( description.getFqcn() ); 1214 1215 if ( !StringTools.isEmpty( description.getBytecode() ) ) 1216 { 1217 buf.append( " BYTECODE " ).append( description.getBytecode() ); 1218 } 1219 1220 buf.append( " X-SCHEMA '" ); 1221 buf.append( getSchemaName( description ) ); 1222 buf.append( "' )" ); 1223 1224 return buf.toString(); 1225 } 1226 1227 1228 private static String getSchemaName( SchemaObject desc ) 1229 { 1230 List<String> values = desc.getExtensions().get( MetaSchemaConstants.X_SCHEMA ); 1231 1232 if ( values == null || values.size() == 0 ) 1233 { 1234 return MetaSchemaConstants.SCHEMA_OTHER; 1235 } 1236 1237 return values.get( 0 ); 1238 } 1239 1240 1241 /** 1242 * Remove the options from the attributeType, and returns the ID. 1243 * 1244 * RFC 4512 : 1245 * attributedescription = attributetype options 1246 * attributetype = oid 1247 * options = *( SEMI option ) 1248 * option = 1*keychar 1249 */ 1250 public static String stripOptions( String attributeId ) 1251 { 1252 int optionsPos = attributeId.indexOf( ";" ); 1253 1254 if ( optionsPos != -1 ) 1255 { 1256 return attributeId.substring( 0, optionsPos ); 1257 } 1258 else 1259 { 1260 return attributeId; 1261 } 1262 } 1263 1264 /** 1265 * Get the options from the attributeType. 1266 * 1267 * For instance, given : 1268 * jpegphoto;binary;lang=jp 1269 * 1270 * your get back a set containing { "binary", "lang=jp" } 1271 */ 1272 public static Set<String> getOptions( String attributeId ) 1273 { 1274 int optionsPos = attributeId.indexOf( ";" ); 1275 1276 if ( optionsPos != -1 ) 1277 { 1278 Set<String> options = new HashSet<String>(); 1279 1280 String[] res = attributeId.substring( optionsPos + 1 ).split( ";" ); 1281 1282 for ( String option:res ) 1283 { 1284 if ( !StringTools.isEmpty( option ) ) 1285 { 1286 options.add( option ); 1287 } 1288 } 1289 1290 return options; 1291 } 1292 else 1293 { 1294 return null; 1295 } 1296 } 1297 1298 1299 /** 1300 * Transform an UUID in a byte array 1301 * @param uuid The UUID to transform 1302 * @return The byte[] representing the UUID 1303 */ 1304 public static byte[] uuidToBytes( UUID uuid ) 1305 { 1306 Long low = uuid.getLeastSignificantBits(); 1307 Long high = uuid.getMostSignificantBits(); 1308 byte[] bytes=new byte[16]; 1309 1310 bytes[0] = (byte) ((high & 0xff00000000000000L)>>56); 1311 bytes[1] = (byte) ((high & 0x00ff000000000000L)>>48); 1312 bytes[2] = (byte) ((high & 0x0000ff0000000000L)>>40); 1313 bytes[3] = (byte) ((high & 0x000000ff00000000L)>>32); 1314 bytes[4] = (byte) ((high & 0x00000000ff000000L)>>24); 1315 bytes[5] = (byte) ((high & 0x0000000000ff0000L)>>16); 1316 bytes[6] = (byte) ((high & 0x000000000000ff00L)>>8); 1317 bytes[7] = (byte) (high & 0x00000000000000ffL); 1318 bytes[8] = (byte) ((low & 0xff00000000000000L)>>56); 1319 bytes[9] = (byte) ((low & 0x00ff000000000000L)>>48); 1320 bytes[10] = (byte) ((low & 0x0000ff0000000000L)>>40); 1321 bytes[11] = (byte) ((low & 0x000000ff00000000L)>>32); 1322 bytes[12] = (byte) ((low & 0x00000000ff000000L)>>24); 1323 bytes[13] = (byte) ((low & 0x0000000000ff0000L)>>16); 1324 bytes[14] = (byte) ((low & 0x000000000000ff00L)>>8); 1325 bytes[15] = (byte) (low & 0x00000000000000ffL); 1326 1327 return bytes; 1328 } 1329 }