001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.api.ldap.model.schema; 021 022 023import java.util.List; 024 025 026/** 027 * Renderer for schema objects. 028 * 029 * Currently the following preconfigured renderers exist: 030 * <ol> 031 * <li> {@link SchemaObjectRenderer#SUBSCHEMA_SUBENTRY_RENDERER}: renders the schema object 032 * without line break and with X-SCHEMA extension. To be used for building subschema subentry. 033 * <li> {@link SchemaObjectRenderer#OPEN_LDAP_SCHEMA_RENDERER}: renders the schema object in OpenLDAP schema 034 * format. That means is starts with schema type and contains line breaks for easier readability. 035 * </ol> 036 * <p> 037 * TODO: currently only {@link ObjectClass} and {@link AttributeType} are supported, implement other schema object types. 038 * 039 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 040 */ 041public final class SchemaObjectRenderer 042{ 043 /** 044 * Preconfigured {@link SchemaObjectRenderer} that renders the schema object without line break and with 045 * X-SCHEMA extension. To be used for building subschema subentry. 046 */ 047 public static final SchemaObjectRenderer SUBSCHEMA_SUBENTRY_RENDERER = new SchemaObjectRenderer( 048 Style.SUBSCHEMA_SUBENTRY_WITH_SCHEMA_NAME ); 049 050 /** 051 * Preconfigured {@link SchemaObjectRenderer} that renders the schema object in OpenLDAP schema format. 052 * That means is starts with schema type and contains line breaks for easier readability. 053 */ 054 public static final SchemaObjectRenderer OPEN_LDAP_SCHEMA_RENDERER = new SchemaObjectRenderer( 055 Style.OPENLDAP_SCHEMA_PRETTY_PRINTED ); 056 057 private enum Style 058 { 059 SUBSCHEMA_SUBENTRY_WITH_SCHEMA_NAME(false, false, true), 060 061 OPENLDAP_SCHEMA_PRETTY_PRINTED(true, true, false); 062 063 final boolean startWithSchemaType; 064 final boolean prettyPrint; 065 final boolean printSchemaName; 066 067 068 Style( boolean startWithSchemaType, boolean prettyPrint, boolean printSchemaName ) 069 { 070 this.startWithSchemaType = startWithSchemaType; 071 this.prettyPrint = prettyPrint; 072 this.printSchemaName = printSchemaName; 073 } 074 } 075 076 private final Style style; 077 078 079 private SchemaObjectRenderer( Style style ) 080 { 081 this.style = style; 082 } 083 084 085 /** 086 * Renders an objectClass according to the Object Class 087 * Description Syntax 1.3.6.1.4.1.1466.115.121.1.37. The syntax is 088 * described in detail within section 4.1.1. of 089 * <a href="https://tools.ietf.org/rfc/rfc4512.txt">RFC 4512</a> 090 * which is replicated here for convenience: 091 * 092 * <pre> 093 * 4.1.1. Object Class Definitions 094 * 095 * Object Class definitions are written according to the ABNF: 096 * 097 * ObjectClassDescription = LPAREN WSP 098 * numericoid ; object identifier 099 * [ SP "NAME" SP qdescrs ] ; short names (descriptors) 100 * [ SP "DESC" SP qdstring ] ; description 101 * [ SP "OBSOLETE" ] ; not active 102 * [ SP "SUP" SP oids ] ; superior object classes 103 * [ SP kind ] ; kind of class 104 * [ SP "MUST" SP oids ] ; attribute types 105 * [ SP "MAY" SP oids ] ; attribute types 106 * extensions WSP RPAREN 107 * 108 * kind = "ABSTRACT" / "STRUCTURAL" / "AUXILIARY" 109 * 110 * where: 111 * <numericoid> is object identifier assigned to this object class; 112 * NAME <qdescrs> are short names (descriptors) identifying this object 113 * class; 114 * DESC <qdstring> is a short descriptive string; 115 * OBSOLETE indicates this object class is not active; 116 * SUP <oids> specifies the direct superclasses of this object class; 117 * the kind of object class is indicated by one of ABSTRACT, 118 * STRUCTURAL, or AUXILIARY, default is STRUCTURAL; 119 * MUST and MAY specify the sets of required and allowed attribute 120 * types, respectively; and 121 * <extensions> describe extensions. 122 * </pre> 123 * @param oc the ObjectClass to render the description of 124 * @return the string form of the Object Class description 125 */ 126 public String render( ObjectClass oc ) 127 { 128 StringBuilder buf = renderStartOidNamesDescObsolete( oc, "objectclass" ); 129 130 renderOids( buf, "SUP", oc.getSuperiorOids() ); 131 132 if ( oc.getType() != null ) 133 { 134 prettyPrintIndent( buf ); 135 buf.append( oc.getType() ); 136 prettyPrintNewLine( buf ); 137 } 138 139 renderOids( buf, "MUST", oc.getMustAttributeTypeOids() ); 140 141 renderOids( buf, "MAY", oc.getMayAttributeTypeOids() ); 142 143 renderXSchemaName( oc, buf ); 144 145 // @todo extensions are not presently supported and skipped 146 // the extensions would go here before closing off the description 147 148 renderClose( buf ); 149 150 return buf.toString(); 151 } 152 153 154 /** 155 * Renders an attributeType according to the 156 * Attribute Type Description Syntax 1.3.6.1.4.1.1466.115.121.1.3. The 157 * syntax is described in detail within section 4.1.2. of 158 * <a href="https://tools.ietf.org/rfc/rfc4512.txt">RFC 4512</a> 159 * which is replicated here for convenience: 160 * 161 * <pre> 162 * 4.1.2. Attribute Types 163 * 164 * Attribute Type definitions are written according to the ABNF: 165 * 166 * AttributeTypeDescription = LPAREN WSP 167 * numericoid ; object identifier 168 * [ SP "NAME" SP qdescrs ] ; short names (descriptors) 169 * [ SP "DESC" SP qdstring ] ; description 170 * [ SP "OBSOLETE" ] ; not active 171 * [ SP "SUP" SP oid ] ; supertype 172 * [ SP "EQUALITY" SP oid ] ; equality matching rule 173 * [ SP "ORDERING" SP oid ] ; ordering matching rule 174 * [ SP "SUBSTR" SP oid ] ; substrings matching rule 175 * [ SP "SYNTAX" SP noidlen ] ; value syntax 176 * [ SP "SINGLE-VALUE" ] ; single-value 177 * [ SP "COLLECTIVE" ] ; collective 178 * [ SP "NO-USER-MODIFICATION" ] ; not user modifiable 179 * [ SP "USAGE" SP usage ] ; usage 180 * extensions WSP RPAREN ; extensions 181 * 182 * usage = "userApplications" / ; user 183 * "directoryOperation" / ; directory operational 184 * "distributedOperation" / ; DSA-shared operational 185 * "dSAOperation" ; DSA-specific operational 186 * 187 * where: 188 * <numericoid> is object identifier assigned to this attribute type; 189 * NAME <qdescrs> are short names (descriptors) identifying this 190 * attribute type; 191 * DESC <qdstring> is a short descriptive string; 192 * OBSOLETE indicates this attribute type is not active; 193 * SUP oid specifies the direct supertype of this type; 194 * EQUALITY, ORDERING, SUBSTR provide the oid of the equality, 195 * ordering, and substrings matching rules, respectively; 196 * SYNTAX identifies value syntax by object identifier and may suggest 197 * a minimum upper bound; 198 * SINGLE-VALUE indicates attributes of this type are restricted to a 199 * single value; 200 * COLLECTIVE indicates this attribute type is collective 201 * [X.501][RFC3671]; 202 * NO-USER-MODIFICATION indicates this attribute type is not user 203 * modifiable; 204 * USAGE indicates the application of this attribute type; and 205 * <extensions> describe extensions. 206 * </pre> 207 * @param at the AttributeType to render the description for 208 * @return the StringBuffer containing the rendered attributeType description 209 */ 210 public String render( AttributeType at ) 211 { 212 StringBuilder buf = renderStartOidNamesDescObsolete( at, "attributetype" ); 213 214 /* 215 * TODO: Check for getSuperior(), getEquality(), getOrdering(), and getSubstring() should not be necessary. 216 * The getXyzOid() methods should return a name but return a numeric OID currently. 217 */ 218 219 if ( at.getSuperior() != null ) 220 { 221 prettyPrintIndent( buf ); 222 buf.append( "SUP " ).append( at.getSuperior().getName() ); 223 prettyPrintNewLine( buf ); 224 } 225 else if ( at.getSuperiorOid() != null ) 226 { 227 prettyPrintIndent( buf ); 228 buf.append( "SUP " ).append( at.getSuperiorOid() ); 229 prettyPrintNewLine( buf ); 230 } 231 232 if ( at.getEquality() != null ) 233 { 234 prettyPrintIndent( buf ); 235 buf.append( "EQUALITY " ).append( at.getEquality().getName() ); 236 prettyPrintNewLine( buf ); 237 } 238 else if ( at.getEqualityOid() != null ) 239 { 240 prettyPrintIndent( buf ); 241 buf.append( "EQUALITY " ).append( at.getEqualityOid() ); 242 prettyPrintNewLine( buf ); 243 } 244 245 if ( at.getOrdering() != null ) 246 { 247 prettyPrintIndent( buf ); 248 buf.append( "ORDERING " ).append( at.getOrdering().getName() ); 249 prettyPrintNewLine( buf ); 250 } 251 else if ( at.getOrderingOid() != null ) 252 { 253 prettyPrintIndent( buf ); 254 buf.append( "ORDERING " ).append( at.getOrderingOid() ); 255 prettyPrintNewLine( buf ); 256 } 257 258 if ( at.getSubstring() != null ) 259 { 260 prettyPrintIndent( buf ); 261 buf.append( "SUBSTR " ).append( at.getSubstring().getName() ); 262 prettyPrintNewLine( buf ); 263 } 264 else if ( at.getSubstringOid() != null ) 265 { 266 prettyPrintIndent( buf ); 267 buf.append( "SUBSTR " ).append( at.getSubstringOid() ); 268 prettyPrintNewLine( buf ); 269 } 270 271 if ( at.getSyntaxOid() != null ) 272 { 273 prettyPrintIndent( buf ); 274 buf.append( "SYNTAX " ).append( at.getSyntaxOid() ); 275 276 if ( at.getSyntaxLength() > 0 ) 277 { 278 buf.append( "{" ).append( at.getSyntaxLength() ).append( "}" ); 279 } 280 prettyPrintNewLine( buf ); 281 } 282 283 if ( at.isSingleValued() ) 284 { 285 prettyPrintIndent( buf ); 286 buf.append( "SINGLE-VALUE" ); 287 prettyPrintNewLine( buf ); 288 } 289 290 if ( at.isCollective() ) 291 { 292 prettyPrintIndent( buf ); 293 buf.append( "COLLECTIVE" ); 294 prettyPrintNewLine( buf ); 295 } 296 297 if ( !at.isUserModifiable() ) 298 { 299 prettyPrintIndent( buf ); 300 buf.append( "NO-USER-MODIFICATION" ); 301 prettyPrintNewLine( buf ); 302 } 303 304 if ( at.getUsage() != null ) 305 { 306 prettyPrintIndent( buf ); 307 buf.append( "USAGE " ).append( UsageEnum.render( at.getUsage() ) ); 308 prettyPrintNewLine( buf ); 309 } 310 311 renderXSchemaName( at, buf ); 312 313 // @todo extensions are not presently supported and skipped 314 // the extensions would go here before closing off the description 315 316 renderClose( buf ); 317 318 return buf.toString(); 319 } 320 321 322 /** 323 * Renders an matchingRule according to the 324 * MatchingRule Description Syntax 1.3.6.1.4.1.1466.115.121.1.30. The syntax 325 * is described in detail within section 4.1.3. 326 * <a href="https://tools.ietf.org/rfc/rfc4512.txt">RFC 4512</a> 327 * which is replicated here for convenience: 328 * 329 * <pre> 330 * 4.1.3. Matching Rules 331 * 332 * Matching rules are used in performance of attribute value assertions, 333 * such as in performance of a Compare operation. They are also used in 334 * evaluation of a Search filters, in determining which individual values 335 * are be added or deleted during performance of a Modify operation, and 336 * used in comparison of distinguished names. 337 * 338 * Each matching rule is identified by an object identifier (OID) and, 339 * optionally, one or more short names (descriptors). 340 * 341 * Matching rule definitions are written according to the ABNF: 342 * 343 * MatchingRuleDescription = LPAREN WSP 344 * numericoid ; object identifier 345 * [ SP "NAME" SP qdescrs ] ; short names (descriptors) 346 * [ SP "DESC" SP qdstring ] ; description 347 * [ SP "OBSOLETE" ] ; not active 348 * SP "SYNTAX" SP numericoid ; assertion syntax 349 * extensions WSP RPAREN ; extensions 350 * 351 * where: 352 * <numericoid> is object identifier assigned to this matching rule; 353 * NAME <qdescrs> are short names (descriptors) identifying this 354 * matching rule; 355 * DESC <qdstring> is a short descriptive string; 356 * OBSOLETE indicates this matching rule is not active; 357 * SYNTAX identifies the assertion syntax (the syntax of the assertion 358 * value) by object identifier; and 359 * <extensions> describe extensions. 360 * </pre> 361 * @param mr the MatchingRule to render the description for 362 * @return the StringBuffer containing the rendered matchingRule description 363 */ 364 public String render( MatchingRule mr ) 365 { 366 StringBuilder buf = renderStartOidNamesDescObsolete( mr, "matchingrule" ); 367 368 prettyPrintIndent( buf ); 369 buf.append( "SYNTAX " ).append( mr.getSyntaxOid() ); 370 prettyPrintNewLine( buf ); 371 372 renderXSchemaName( mr, buf ); 373 374 // @todo extensions are not presently supported and skipped 375 // the extensions would go here before closing off the description 376 377 renderClose( buf ); 378 379 return buf.toString(); 380 } 381 382 383 /** 384 * Renders a Syntax according to the LDAP Syntax 385 * Description Syntax 1.3.6.1.4.1.1466.115.121.1.54. The syntax is described 386 * in detail within section 4.1.5. of 387 * <a href="https://tools.ietf.org/rfc/rfc4512.txt">RFC 4512</a> 388 * which is replicated here for convenience: 389 * 390 * <pre> 391 * LDAP syntax definitions are written according to the ABNF: 392 * 393 * SyntaxDescription = LPAREN WSP 394 * numericoid ; object identifier 395 * [ SP "DESC" SP qdstring ] ; description 396 * extensions WSP RPAREN ; extensions 397 * 398 * where: 399 * <numericoid> is the object identifier assigned to this LDAP syntax; 400 * DESC <qdstring> is a short descriptive string; and 401 * <extensions> describe extensions. 402 * </pre> 403 * @param syntax the Syntax to render the description for 404 * @return the StringBuffer containing the rendered syntax description 405 */ 406 public String render( LdapSyntax syntax ) 407 { 408 StringBuilder buf = new StringBuilder(); 409 410 if ( style.startWithSchemaType ) 411 { 412 buf.append( "ldapsyntax " ); 413 } 414 415 buf.append( "( " ).append( syntax.getOid() ); 416 prettyPrintNewLine( buf ); 417 418 renderDescription( syntax, buf ); 419 420 renderXSchemaName( syntax, buf ); 421 422 prettyPrintIndent( buf ); 423 if ( syntax.isHumanReadable() ) 424 { 425 buf.append( "X-NOT-HUMAN-READABLE 'false'" ); 426 } 427 else 428 { 429 buf.append( "X-NOT-HUMAN-READABLE 'true'" ); 430 } 431 prettyPrintNewLine( buf ); 432 433 // @todo extensions are not presently supported and skipped 434 // the extensions would go here before closing off the description 435 436 renderClose( buf ); 437 438 return buf.toString(); 439 } 440 441 442 /** 443 * NOT FULLY IMPLEMENTED! 444 * Renders a MatchingRuleUse as a String 445 * 446 * @param mru The MatchingRuleUse to render 447 * @return The MatchingRuleUse as a String 448 */ 449 public String render( MatchingRuleUse mru ) 450 { 451 StringBuilder buf = renderStartOidNamesDescObsolete( mru, "matchingruleuse" ); 452 453 List<String> applies = mru.getApplicableAttributeOids(); 454 455 if ( ( applies != null ) && !applies.isEmpty() ) 456 { 457 prettyPrintIndent( buf ); 458 buf.append( "APPLIES " ); 459 renderOids( buf, applies ); 460 prettyPrintNewLine( buf ); 461 } 462 463 renderXSchemaName( mru, buf ); 464 465 // @todo extensions are not presently supported and skipped 466 // the extensions would go here before closing off the description 467 468 renderClose( buf ); 469 470 return buf.toString(); 471 } 472 473 474 /** 475 * NOT FULLY IMPLEMENTED! 476 * Renders a DitContentRule as a String 477 * 478 * @param dcr The DitContentRule to render 479 * @return The DitContentRule as a String 480 */ 481 public String render( DitContentRule dcr ) 482 { 483 StringBuilder buf = renderStartOidNamesDescObsolete( dcr, "ditcontentrule" ); 484 485 renderOids( buf, "AUX", dcr.getAuxObjectClassOids() ); 486 487 renderOids( buf, "MUST", dcr.getMustAttributeTypeOids() ); 488 489 renderOids( buf, "MAY", dcr.getMayAttributeTypeOids() ); 490 491 renderOids( buf, "NOT", dcr.getNotAttributeTypeOids() ); 492 493 renderXSchemaName( dcr, buf ); 494 495 // @todo extensions are not presently supported and skipped 496 // the extensions would go here before closing off the description 497 498 renderClose( buf ); 499 500 return buf.toString(); 501 } 502 503 504 /** 505 * NOT FULLY IMPLEMENTED! 506 * 507 * @param dsr The DitStructureRule to render 508 * @return The DitStructureRule as a String 509 */ 510 public String render( DitStructureRule dsr ) 511 { 512 StringBuilder buf = new StringBuilder(); 513 514 if ( style.startWithSchemaType ) 515 { 516 buf.append( "ditstructurerule " ); 517 } 518 519 buf.append( "( " ).append( dsr.getRuleId() ); 520 521 renderNames( dsr, buf ); 522 523 renderDescription( dsr, buf ); 524 525 renderObsolete( dsr, buf ); 526 527 prettyPrintIndent( buf ); 528 buf.append( "FORM " ).append( dsr.getForm() ); 529 prettyPrintNewLine( buf ); 530 531 renderRuleIds( buf, dsr.getSuperRules() ); 532 533 renderXSchemaName( dsr, buf ); 534 535 // @todo extensions are not presently supported and skipped 536 // the extensions would go here before closing off the description 537 538 renderClose( buf ); 539 540 return buf.toString(); 541 } 542 543 544 /** 545 * NOT FULLY IMPLEMENTED! 546 * Render a NameForm as a String 547 * 548 * @param nf The NameForm to render 549 * @return The rendered String 550 */ 551 public String render( NameForm nf ) 552 { 553 StringBuilder buf = renderStartOidNamesDescObsolete( nf, "nameform" ); 554 555 prettyPrintIndent( buf ); 556 buf.append( "OC " ).append( nf.getStructuralObjectClassOid() ); 557 prettyPrintNewLine( buf ); 558 559 renderOids( buf, "MUST", nf.getMustAttributeTypeOids() ); 560 561 renderOids( buf, "MAY", nf.getMayAttributeTypeOids() ); 562 563 renderXSchemaName( nf, buf ); 564 565 renderClose( buf ); 566 567 return buf.toString(); 568 } 569 570 571 private StringBuilder renderStartOidNamesDescObsolete( SchemaObject so, String schemaObjectType ) 572 { 573 StringBuilder buf = new StringBuilder(); 574 575 if ( style.startWithSchemaType ) 576 { 577 buf.append( schemaObjectType ).append( ' ' ); 578 } 579 580 buf.append( "( " ).append( so.getOid() ); 581 582 renderNames( so, buf ); 583 584 renderDescription( so, buf ); 585 586 renderObsolete( so, buf ); 587 return buf; 588 } 589 590 591 private void renderNames( SchemaObject so, StringBuilder buf ) 592 { 593 List<String> names = so.getNames(); 594 595 if ( ( names != null ) && !names.isEmpty() ) 596 { 597 buf.append( " NAME " ); 598 renderQDescrs( buf, names ); 599 prettyPrintNewLine( buf ); 600 } 601 else 602 { 603 prettyPrintNewLine( buf ); 604 } 605 } 606 607 608 private void renderDescription( SchemaObject so, StringBuilder buf ) 609 { 610 if ( so.getDescription() != null ) 611 { 612 prettyPrintIndent( buf ); 613 buf.append( "DESC " ); 614 renderQDString( buf, so.getDescription() ); 615 prettyPrintNewLine( buf ); 616 } 617 } 618 619 620 private void renderObsolete( SchemaObject so, StringBuilder buf ) 621 { 622 if ( so.isObsolete() ) 623 { 624 prettyPrintIndent( buf ); 625 buf.append( "OBSOLETE" ); 626 prettyPrintNewLine( buf ); 627 } 628 } 629 630 631 private void prettyPrintNewLine( StringBuilder buf ) 632 { 633 if ( style.prettyPrint ) 634 { 635 buf.append( '\n' ); 636 } 637 else 638 { 639 buf.append( " " ); 640 } 641 } 642 643 644 private void prettyPrintIndent( StringBuilder buf ) 645 { 646 if ( style.prettyPrint ) 647 { 648 buf.append( "\t" ); 649 } 650 } 651 652 653 /** 654 * Renders qdescrs into a new buffer.<br> 655 * <pre> 656 * descrs ::= qdescr | '(' WSP qdescrlist WSP ')' 657 * qdescrlist ::= [ qdescr ( SP qdescr )* ] 658 * qdescr ::= SQUOTE descr SQUOTE 659 * </pre> 660 * @param qdescrs the quoted description strings to render 661 * @return the string buffer the qdescrs are rendered into 662 */ 663 private StringBuilder renderQDescrs( StringBuilder buf, List<String> qdescrs ) 664 { 665 if ( ( qdescrs == null ) || qdescrs.isEmpty() ) 666 { 667 return buf; 668 } 669 670 if ( qdescrs.size() == 1 ) 671 { 672 buf.append( '\'' ).append( qdescrs.get( 0 ) ).append( '\'' ); 673 } 674 else 675 { 676 buf.append( "( " ); 677 678 for ( String qdescr : qdescrs ) 679 { 680 buf.append( '\'' ).append( qdescr ).append( "' " ); 681 } 682 683 buf.append( ")" ); 684 } 685 686 return buf; 687 } 688 689 690 private void renderOids( StringBuilder buf, String prefix, List<String> oids ) 691 { 692 if ( ( oids != null ) && !oids.isEmpty() ) 693 { 694 prettyPrintIndent( buf ); 695 buf.append( prefix ).append( ' ' ); 696 renderOids( buf, oids ); 697 prettyPrintNewLine( buf ); 698 } 699 } 700 701 702 /** 703 * Renders oids into a new buffer.<br> 704 * <pre> 705 * oids ::= oid | '(' WSP oidlist WSP ')' 706 * oidlist ::= oid ( WSP '$' WSP oid )* 707 * </pre> 708 * 709 * @param qdescrs the quoted description strings to render 710 * @return the string buffer the qdescrs are rendered into 711 */ 712 private StringBuilder renderOids( StringBuilder buf, List<String> oids ) 713 { 714 if ( oids.size() == 1 ) 715 { 716 buf.append( oids.get( 0 ) ); 717 } 718 else 719 { 720 buf.append( "( " ); 721 722 boolean isFirst = true; 723 724 for ( String oid : oids ) 725 { 726 if ( isFirst ) 727 { 728 isFirst = false; 729 } 730 else 731 { 732 buf.append( " $ " ); 733 } 734 735 buf.append( oid ); 736 } 737 738 buf.append( " )" ); 739 } 740 741 return buf; 742 } 743 744 745 /** 746 * Renders QDString into a new buffer.<br> 747 * 748 * @param qdescrs the quoted description strings to render 749 * @return the string buffer the qdescrs are rendered into 750 */ 751 private StringBuilder renderQDString( StringBuilder buf, String qdString ) 752 { 753 buf.append( '\'' ); 754 755 for ( char c : qdString.toCharArray() ) 756 { 757 switch ( c ) 758 { 759 case 0x27: 760 buf.append( "\\27" ); 761 break; 762 763 case 0x5C: 764 buf.append( "\\5C" ); 765 break; 766 767 default: 768 buf.append( c ); 769 break; 770 } 771 } 772 773 buf.append( '\'' ); 774 775 return buf; 776 } 777 778 779 private StringBuilder renderRuleIds( StringBuilder buf, List<Integer> ruleIds ) 780 { 781 if ( ( ruleIds != null ) && !ruleIds.isEmpty() ) 782 { 783 prettyPrintIndent( buf ); 784 buf.append( "SUP " ); 785 786 if ( ruleIds.size() == 1 ) 787 { 788 buf.append( ruleIds.get( 0 ) ); 789 } 790 else 791 { 792 buf.append( "( " ); 793 794 boolean isFirst = true; 795 796 for ( Integer ruleId : ruleIds ) 797 { 798 if ( isFirst ) 799 { 800 isFirst = false; 801 } 802 else 803 { 804 buf.append( " " ); 805 } 806 807 buf.append( ruleId ); 808 } 809 810 buf.append( " )" ); 811 } 812 813 prettyPrintNewLine( buf ); 814 } 815 816 return buf; 817 } 818 819 820 private void renderXSchemaName( SchemaObject oc, StringBuilder buf ) 821 { 822 if ( style.printSchemaName ) 823 { 824 prettyPrintIndent( buf ); 825 buf.append( "X-SCHEMA '" ); 826 buf.append( oc.getSchemaName() ); 827 buf.append( "'" ); 828 prettyPrintNewLine( buf ); 829 } 830 } 831 832 833 private void renderClose( StringBuilder buf ) 834 { 835 if ( ( style.prettyPrint ) && ( buf.charAt( buf.length() - 1 ) == '\n' ) ) 836 { 837 buf.deleteCharAt( buf.length() - 1 ); 838 buf.append( " " ); 839 } 840 841 buf.append( ")" ); 842 } 843 844}