View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.api.ldap.model.schema;
21  
22  
23  import java.util.List;
24  
25  import org.apache.directory.api.ldap.model.exception.LdapException;
26  
27  
28  /**
29   * Renderer for schema objects.
30   * 
31   * Currently the following preconfigured renderers exist: 
32   * <ol>
33   * <li> {@link SchemaObjectRenderer#SUBSCHEMA_SUBENTRY_RENDERER}: renders the schema object 
34   *      without line break and with X-SCHEMA extension. To be used for building subschema subentry.
35   * <li> {@link SchemaObjectRenderer#OPEN_LDAP_SCHEMA_RENDERER}: renders the schema object in OpenLDAP schema  
36   *      format. That means is starts with schema type and contains line breaks for easier readability.
37   * </ol>
38   * <p>
39   * TODO: currently only {@link ObjectClass} and {@link AttributeType} are supported, implement other schema object types.
40   * 
41   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
42   */
43  public class SchemaObjectRenderer
44  {
45      /**
46       * Preconfigured {@link SchemaObjectRenderer} that renders the schema object without line break and with
47       * X-SCHEMA extension. To be used for building subschema subentry.
48       */
49      public static final SchemaObjectRenderer SUBSCHEMA_SUBENTRY_RENDERER = new SchemaObjectRenderer(
50          Style.SUBSCHEMA_SUBENTRY_WITH_SCHEMA_NAME );
51  
52      /**
53       * Preconfigured {@link SchemaObjectRenderer} that renders the schema object in OpenLDAP schema format. 
54       * That means is starts with schema type and contains line breaks for easier readability.
55       */
56      public static final SchemaObjectRenderer OPEN_LDAP_SCHEMA_RENDERER = new SchemaObjectRenderer(
57          Style.OPENLDAP_SCHEMA_PRETTY_PRINTED );
58  
59      private enum Style
60      {
61          SUBSCHEMA_SUBENTRY_WITH_SCHEMA_NAME(false, false, true),
62  
63          OPENLDAP_SCHEMA_PRETTY_PRINTED(true, true, false);
64  
65          final boolean startWithSchemaType;
66          final boolean prettyPrint;
67          final boolean printSchemaName;
68  
69  
70          private Style( boolean startWithSchemaType, boolean prettyPrint, boolean printSchemaName )
71          {
72              this.startWithSchemaType = startWithSchemaType;
73              this.prettyPrint = prettyPrint;
74              this.printSchemaName = printSchemaName;
75          }
76      }
77  
78      private final Style style;
79  
80  
81      private SchemaObjectRenderer( Style style )
82      {
83          this.style = style;
84      }
85  
86  
87      /**
88       * Renders an objectClass according to the Object Class 
89       * Description Syntax 1.3.6.1.4.1.1466.115.121.1.37. The syntax is
90       * described in detail within section 4.1.1. of 
91       * <a href="https://tools.ietf.org/rfc/rfc4512.txt">RFC 4512</a>
92       * which is replicated here for convenience:
93       * 
94       * <pre>
95       *  4.1.1. Object Class Definitions
96       * 
97       *   Object Class definitions are written according to the ABNF:
98       * 
99       *     ObjectClassDescription = LPAREN WSP
100      *         numericoid                 ; object identifier
101      *         [ SP &quot;NAME&quot; SP qdescrs ]   ; short names (descriptors)
102      *         [ SP &quot;DESC&quot; SP qdstring ]  ; description
103      *         [ SP &quot;OBSOLETE&quot; ]          ; not active
104      *         [ SP &quot;SUP&quot; SP oids ]       ; superior object classes
105      *         [ SP kind ]                ; kind of class
106      *         [ SP &quot;MUST&quot; SP oids ]      ; attribute types
107      *         [ SP &quot;MAY&quot; SP oids ]       ; attribute types
108      *         extensions WSP RPAREN
109      * 
110      *     kind = &quot;ABSTRACT&quot; / &quot;STRUCTURAL&quot; / &quot;AUXILIARY&quot;
111      * 
112      *   where:
113      *     &lt;numericoid&gt; is object identifier assigned to this object class;
114      *     NAME &lt;qdescrs&gt; are short names (descriptors) identifying this object
115      *         class;
116      *     DESC &lt;qdstring&gt; is a short descriptive string;
117      *     OBSOLETE indicates this object class is not active;
118      *     SUP &lt;oids&gt; 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      *     &lt;extensions&gt; 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 &quot;NAME&quot; SP qdescrs ]      ; short names (descriptors)
171      *         [ SP &quot;DESC&quot; SP qdstring ]     ; description
172      *         [ SP &quot;OBSOLETE&quot; ]             ; not active
173      *         [ SP &quot;SUP&quot; SP oid ]           ; supertype
174      *         [ SP &quot;EQUALITY&quot; SP oid ]      ; equality matching rule
175      *         [ SP &quot;ORDERING&quot; SP oid ]      ; ordering matching rule
176      *         [ SP &quot;SUBSTR&quot; SP oid ]        ; substrings matching rule
177      *         [ SP &quot;SYNTAX&quot; SP noidlen ]    ; value syntax
178      *         [ SP &quot;SINGLE-VALUE&quot; ]         ; single-value
179      *         [ SP &quot;COLLECTIVE&quot; ]           ; collective
180      *         [ SP &quot;NO-USER-MODIFICATION&quot; ] ; not user modifiable
181      *         [ SP &quot;USAGE&quot; SP usage ]       ; usage
182      *         extensions WSP RPAREN         ; extensions
183      * 
184      *     usage = &quot;userApplications&quot;     /  ; user
185      *             &quot;directoryOperation&quot;   /  ; directory operational
186      *             &quot;distributedOperation&quot; /  ; DSA-shared operational
187      *             &quot;dSAOperation&quot;            ; DSA-specific operational
188      * 
189      *   where:
190      *     &lt;numericoid&gt; is object identifier assigned to this attribute type;
191      *     NAME &lt;qdescrs&gt; are short names (descriptors) identifying this
192      *         attribute type;
193      *     DESC &lt;qdstring&gt; 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      *     &lt;extensions&gt; 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 &quot;NAME&quot; SP qdescrs ]   ; short names (descriptors)
350      *         [ SP &quot;DESC&quot; SP qdstring ]  ; description
351      *         [ SP &quot;OBSOLETE&quot; ]          ; not active
352      *         SP &quot;SYNTAX&quot; SP numericoid  ; assertion syntax
353      *         extensions WSP RPAREN      ; extensions
354      * 
355      *   where:
356      *     &lt;numericoid&gt; is object identifier assigned to this matching rule;
357      *     NAME &lt;qdescrs&gt; are short names (descriptors) identifying this
358      *         matching rule;
359      *     DESC &lt;qdstring&gt; 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      *     &lt;extensions&gt; 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 &quot;DESC&quot; SP qdstring ]  ; description
402      *       extensions WSP RPAREN      ; extensions
403      * 
404      *  where:
405      *   &lt;numericoid&gt; is the object identifier assigned to this LDAP syntax;
406      *   DESC &lt;qdstring&gt; is a short descriptive string; and
407      *   &lt;extensions&gt; 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 }