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