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