| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| package org.apache.directory.api.ldap.model.schema; |
| |
| |
| import java.util.List; |
| |
| |
| /** |
| * Renderer for schema objects. |
| * |
| * Currently the following preconfigured renderers exist: |
| * <ol> |
| * <li> {@link SchemaObjectRenderer#SUBSCHEMA_SUBENTRY_RENDERER}: renders the schema object |
| * without line break and with X-SCHEMA extension. To be used for building subschema subentry. |
| * <li> {@link SchemaObjectRenderer#OPEN_LDAP_SCHEMA_RENDERER}: renders the schema object in OpenLDAP schema |
| * format. That means is starts with schema type and contains line breaks for easier readability. |
| * </ol> |
| * <p> |
| * TODO: currently only {@link ObjectClass} and {@link AttributeType} are supported, implement other schema object types. |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| */ |
| public final class SchemaObjectRenderer |
| { |
| /** |
| * Preconfigured {@link SchemaObjectRenderer} that renders the schema object without line break and with |
| * X-SCHEMA extension. To be used for building subschema subentry. |
| */ |
| public static final SchemaObjectRenderer SUBSCHEMA_SUBENTRY_RENDERER = new SchemaObjectRenderer( |
| Style.SUBSCHEMA_SUBENTRY_WITH_SCHEMA_NAME ); |
| |
| /** |
| * Preconfigured {@link SchemaObjectRenderer} that renders the schema object in OpenLDAP schema format. |
| * That means is starts with schema type and contains line breaks for easier readability. |
| */ |
| public static final SchemaObjectRenderer OPEN_LDAP_SCHEMA_RENDERER = new SchemaObjectRenderer( |
| Style.OPENLDAP_SCHEMA_PRETTY_PRINTED ); |
| |
| private enum Style |
| { |
| SUBSCHEMA_SUBENTRY_WITH_SCHEMA_NAME(false, false, true), |
| |
| OPENLDAP_SCHEMA_PRETTY_PRINTED(true, true, false); |
| |
| final boolean startWithSchemaType; |
| final boolean prettyPrint; |
| final boolean printSchemaName; |
| |
| |
| Style( boolean startWithSchemaType, boolean prettyPrint, boolean printSchemaName ) |
| { |
| this.startWithSchemaType = startWithSchemaType; |
| this.prettyPrint = prettyPrint; |
| this.printSchemaName = printSchemaName; |
| } |
| } |
| |
| private final Style style; |
| |
| |
| private SchemaObjectRenderer( Style style ) |
| { |
| this.style = style; |
| } |
| |
| |
| /** |
| * Renders an objectClass according to the Object Class |
| * Description Syntax 1.3.6.1.4.1.1466.115.121.1.37. The syntax is |
| * described in detail within section 4.1.1. of |
| * <a href="https://tools.ietf.org/rfc/rfc4512.txt">RFC 4512</a> |
| * which is replicated here for convenience: |
| * |
| * <pre> |
| * 4.1.1. Object Class Definitions |
| * |
| * Object Class definitions are written according to the ABNF: |
| * |
| * ObjectClassDescription = LPAREN WSP |
| * numericoid ; object identifier |
| * [ SP "NAME" SP qdescrs ] ; short names (descriptors) |
| * [ SP "DESC" SP qdstring ] ; description |
| * [ SP "OBSOLETE" ] ; not active |
| * [ SP "SUP" SP oids ] ; superior object classes |
| * [ SP kind ] ; kind of class |
| * [ SP "MUST" SP oids ] ; attribute types |
| * [ SP "MAY" SP oids ] ; attribute types |
| * extensions WSP RPAREN |
| * |
| * kind = "ABSTRACT" / "STRUCTURAL" / "AUXILIARY" |
| * |
| * where: |
| * <numericoid> is object identifier assigned to this object class; |
| * NAME <qdescrs> are short names (descriptors) identifying this object |
| * class; |
| * DESC <qdstring> is a short descriptive string; |
| * OBSOLETE indicates this object class is not active; |
| * SUP <oids> specifies the direct superclasses of this object class; |
| * the kind of object class is indicated by one of ABSTRACT, |
| * STRUCTURAL, or AUXILIARY, default is STRUCTURAL; |
| * MUST and MAY specify the sets of required and allowed attribute |
| * types, respectively; and |
| * <extensions> describe extensions. |
| * </pre> |
| * @param oc the ObjectClass to render the description of |
| * @return the string form of the Object Class description |
| */ |
| public String render( ObjectClass oc ) |
| { |
| StringBuilder buf = renderStartOidNamesDescObsolete( oc, "objectclass" ); |
| |
| renderOids( buf, "SUP", oc.getSuperiorOids() ); |
| |
| if ( oc.getType() != null ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( oc.getType() ); |
| prettyPrintNewLine( buf ); |
| } |
| |
| renderOids( buf, "MUST", oc.getMustAttributeTypeOids() ); |
| |
| renderOids( buf, "MAY", oc.getMayAttributeTypeOids() ); |
| |
| renderXSchemaName( oc, buf ); |
| |
| // @todo extensions are not presently supported and skipped |
| // the extensions would go here before closing off the description |
| |
| renderClose( buf ); |
| |
| return buf.toString(); |
| } |
| |
| |
| /** |
| * Renders an attributeType according to the |
| * Attribute Type Description Syntax 1.3.6.1.4.1.1466.115.121.1.3. The |
| * syntax is described in detail within section 4.1.2. of |
| * <a href="https://tools.ietf.org/rfc/rfc4512.txt">RFC 4512</a> |
| * which is replicated here for convenience: |
| * |
| * <pre> |
| * 4.1.2. Attribute Types |
| * |
| * Attribute Type definitions are written according to the ABNF: |
| * |
| * AttributeTypeDescription = LPAREN WSP |
| * numericoid ; object identifier |
| * [ SP "NAME" SP qdescrs ] ; short names (descriptors) |
| * [ SP "DESC" SP qdstring ] ; description |
| * [ SP "OBSOLETE" ] ; not active |
| * [ SP "SUP" SP oid ] ; supertype |
| * [ SP "EQUALITY" SP oid ] ; equality matching rule |
| * [ SP "ORDERING" SP oid ] ; ordering matching rule |
| * [ SP "SUBSTR" SP oid ] ; substrings matching rule |
| * [ SP "SYNTAX" SP noidlen ] ; value syntax |
| * [ SP "SINGLE-VALUE" ] ; single-value |
| * [ SP "COLLECTIVE" ] ; collective |
| * [ SP "NO-USER-MODIFICATION" ] ; not user modifiable |
| * [ SP "USAGE" SP usage ] ; usage |
| * extensions WSP RPAREN ; extensions |
| * |
| * usage = "userApplications" / ; user |
| * "directoryOperation" / ; directory operational |
| * "distributedOperation" / ; DSA-shared operational |
| * "dSAOperation" ; DSA-specific operational |
| * |
| * where: |
| * <numericoid> is object identifier assigned to this attribute type; |
| * NAME <qdescrs> are short names (descriptors) identifying this |
| * attribute type; |
| * DESC <qdstring> is a short descriptive string; |
| * OBSOLETE indicates this attribute type is not active; |
| * SUP oid specifies the direct supertype of this type; |
| * EQUALITY, ORDERING, SUBSTR provide the oid of the equality, |
| * ordering, and substrings matching rules, respectively; |
| * SYNTAX identifies value syntax by object identifier and may suggest |
| * a minimum upper bound; |
| * SINGLE-VALUE indicates attributes of this type are restricted to a |
| * single value; |
| * COLLECTIVE indicates this attribute type is collective |
| * [X.501][RFC3671]; |
| * NO-USER-MODIFICATION indicates this attribute type is not user |
| * modifiable; |
| * USAGE indicates the application of this attribute type; and |
| * <extensions> describe extensions. |
| * </pre> |
| * @param at the AttributeType to render the description for |
| * @return the StringBuffer containing the rendered attributeType description |
| */ |
| public String render( AttributeType at ) |
| { |
| StringBuilder buf = renderStartOidNamesDescObsolete( at, "attributetype" ); |
| |
| /* |
| * TODO: Check for getSuperior(), getEquality(), getOrdering(), and getSubstring() should not be necessary. |
| * The getXyzOid() methods should return a name but return a numeric OID currently. |
| */ |
| |
| if ( at.getSuperior() != null ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "SUP " ).append( at.getSuperior().getName() ); |
| prettyPrintNewLine( buf ); |
| } |
| else if ( at.getSuperiorOid() != null ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "SUP " ).append( at.getSuperiorOid() ); |
| prettyPrintNewLine( buf ); |
| } |
| |
| if ( at.getEquality() != null ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "EQUALITY " ).append( at.getEquality().getName() ); |
| prettyPrintNewLine( buf ); |
| } |
| else if ( at.getEqualityOid() != null ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "EQUALITY " ).append( at.getEqualityOid() ); |
| prettyPrintNewLine( buf ); |
| } |
| |
| if ( at.getOrdering() != null ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "ORDERING " ).append( at.getOrdering().getName() ); |
| prettyPrintNewLine( buf ); |
| } |
| else if ( at.getOrderingOid() != null ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "ORDERING " ).append( at.getOrderingOid() ); |
| prettyPrintNewLine( buf ); |
| } |
| |
| if ( at.getSubstring() != null ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "SUBSTR " ).append( at.getSubstring().getName() ); |
| prettyPrintNewLine( buf ); |
| } |
| else if ( at.getSubstringOid() != null ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "SUBSTR " ).append( at.getSubstringOid() ); |
| prettyPrintNewLine( buf ); |
| } |
| |
| if ( at.getSyntaxOid() != null ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "SYNTAX " ).append( at.getSyntaxOid() ); |
| |
| if ( at.getSyntaxLength() > 0 ) |
| { |
| buf.append( "{" ).append( at.getSyntaxLength() ).append( "}" ); |
| } |
| prettyPrintNewLine( buf ); |
| } |
| |
| if ( at.isSingleValued() ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "SINGLE-VALUE" ); |
| prettyPrintNewLine( buf ); |
| } |
| |
| if ( at.isCollective() ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "COLLECTIVE" ); |
| prettyPrintNewLine( buf ); |
| } |
| |
| if ( !at.isUserModifiable() ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "NO-USER-MODIFICATION" ); |
| prettyPrintNewLine( buf ); |
| } |
| |
| if ( at.getUsage() != null ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "USAGE " ).append( UsageEnum.render( at.getUsage() ) ); |
| prettyPrintNewLine( buf ); |
| } |
| |
| renderXSchemaName( at, buf ); |
| |
| // @todo extensions are not presently supported and skipped |
| // the extensions would go here before closing off the description |
| |
| renderClose( buf ); |
| |
| return buf.toString(); |
| } |
| |
| |
| /** |
| * Renders an matchingRule according to the |
| * MatchingRule Description Syntax 1.3.6.1.4.1.1466.115.121.1.30. The syntax |
| * is described in detail within section 4.1.3. |
| * <a href="https://tools.ietf.org/rfc/rfc4512.txt">RFC 4512</a> |
| * which is replicated here for convenience: |
| * |
| * <pre> |
| * 4.1.3. Matching Rules |
| * |
| * Matching rules are used in performance of attribute value assertions, |
| * such as in performance of a Compare operation. They are also used in |
| * evaluation of a Search filters, in determining which individual values |
| * are be added or deleted during performance of a Modify operation, and |
| * used in comparison of distinguished names. |
| * |
| * Each matching rule is identified by an object identifier (OID) and, |
| * optionally, one or more short names (descriptors). |
| * |
| * Matching rule definitions are written according to the ABNF: |
| * |
| * MatchingRuleDescription = LPAREN WSP |
| * numericoid ; object identifier |
| * [ SP "NAME" SP qdescrs ] ; short names (descriptors) |
| * [ SP "DESC" SP qdstring ] ; description |
| * [ SP "OBSOLETE" ] ; not active |
| * SP "SYNTAX" SP numericoid ; assertion syntax |
| * extensions WSP RPAREN ; extensions |
| * |
| * where: |
| * <numericoid> is object identifier assigned to this matching rule; |
| * NAME <qdescrs> are short names (descriptors) identifying this |
| * matching rule; |
| * DESC <qdstring> is a short descriptive string; |
| * OBSOLETE indicates this matching rule is not active; |
| * SYNTAX identifies the assertion syntax (the syntax of the assertion |
| * value) by object identifier; and |
| * <extensions> describe extensions. |
| * </pre> |
| * @param mr the MatchingRule to render the description for |
| * @return the StringBuffer containing the rendered matchingRule description |
| */ |
| public String render( MatchingRule mr ) |
| { |
| StringBuilder buf = renderStartOidNamesDescObsolete( mr, "matchingrule" ); |
| |
| prettyPrintIndent( buf ); |
| buf.append( "SYNTAX " ).append( mr.getSyntaxOid() ); |
| prettyPrintNewLine( buf ); |
| |
| renderXSchemaName( mr, buf ); |
| |
| // @todo extensions are not presently supported and skipped |
| // the extensions would go here before closing off the description |
| |
| renderClose( buf ); |
| |
| return buf.toString(); |
| } |
| |
| |
| /** |
| * Renders a Syntax according to the LDAP Syntax |
| * Description Syntax 1.3.6.1.4.1.1466.115.121.1.54. The syntax is described |
| * in detail within section 4.1.5. of |
| * <a href="https://tools.ietf.org/rfc/rfc4512.txt">RFC 4512</a> |
| * which is replicated here for convenience: |
| * |
| * <pre> |
| * LDAP syntax definitions are written according to the ABNF: |
| * |
| * SyntaxDescription = LPAREN WSP |
| * numericoid ; object identifier |
| * [ SP "DESC" SP qdstring ] ; description |
| * extensions WSP RPAREN ; extensions |
| * |
| * where: |
| * <numericoid> is the object identifier assigned to this LDAP syntax; |
| * DESC <qdstring> is a short descriptive string; and |
| * <extensions> describe extensions. |
| * </pre> |
| * @param syntax the Syntax to render the description for |
| * @return the StringBuffer containing the rendered syntax description |
| */ |
| public String render( LdapSyntax syntax ) |
| { |
| StringBuilder buf = new StringBuilder(); |
| |
| if ( style.startWithSchemaType ) |
| { |
| buf.append( "ldapsyntax " ); |
| } |
| |
| buf.append( "( " ).append( syntax.getOid() ); |
| prettyPrintNewLine( buf ); |
| |
| renderDescription( syntax, buf ); |
| |
| renderXSchemaName( syntax, buf ); |
| |
| prettyPrintIndent( buf ); |
| if ( syntax.isHumanReadable() ) |
| { |
| buf.append( "X-NOT-HUMAN-READABLE 'false'" ); |
| } |
| else |
| { |
| buf.append( "X-NOT-HUMAN-READABLE 'true'" ); |
| } |
| prettyPrintNewLine( buf ); |
| |
| // @todo extensions are not presently supported and skipped |
| // the extensions would go here before closing off the description |
| |
| renderClose( buf ); |
| |
| return buf.toString(); |
| } |
| |
| |
| /** |
| * NOT FULLY IMPLEMENTED! |
| * Renders a MatchingRuleUse as a String |
| * |
| * @param mru The MatchingRuleUse to render |
| * @return The MatchingRuleUse as a String |
| */ |
| public String render( MatchingRuleUse mru ) |
| { |
| StringBuilder buf = renderStartOidNamesDescObsolete( mru, "matchingruleuse" ); |
| |
| List<String> applies = mru.getApplicableAttributeOids(); |
| |
| if ( ( applies != null ) && !applies.isEmpty() ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "APPLIES " ); |
| renderOids( buf, applies ); |
| prettyPrintNewLine( buf ); |
| } |
| |
| renderXSchemaName( mru, buf ); |
| |
| // @todo extensions are not presently supported and skipped |
| // the extensions would go here before closing off the description |
| |
| renderClose( buf ); |
| |
| return buf.toString(); |
| } |
| |
| |
| /** |
| * NOT FULLY IMPLEMENTED! |
| * Renders a DitContentRule as a String |
| * |
| * @param dcr The DitContentRule to render |
| * @return The DitContentRule as a String |
| */ |
| public String render( DitContentRule dcr ) |
| { |
| StringBuilder buf = renderStartOidNamesDescObsolete( dcr, "ditcontentrule" ); |
| |
| renderOids( buf, "AUX", dcr.getAuxObjectClassOids() ); |
| |
| renderOids( buf, "MUST", dcr.getMustAttributeTypeOids() ); |
| |
| renderOids( buf, "MAY", dcr.getMayAttributeTypeOids() ); |
| |
| renderOids( buf, "NOT", dcr.getNotAttributeTypeOids() ); |
| |
| renderXSchemaName( dcr, buf ); |
| |
| // @todo extensions are not presently supported and skipped |
| // the extensions would go here before closing off the description |
| |
| renderClose( buf ); |
| |
| return buf.toString(); |
| } |
| |
| |
| /** |
| * NOT FULLY IMPLEMENTED! |
| * |
| * @param dsr The DitStructureRule to render |
| * @return The DitStructureRule as a String |
| */ |
| public String render( DitStructureRule dsr ) |
| { |
| StringBuilder buf = new StringBuilder(); |
| |
| if ( style.startWithSchemaType ) |
| { |
| buf.append( "ditstructurerule " ); |
| } |
| |
| buf.append( "( " ).append( dsr.getRuleId() ); |
| |
| renderNames( dsr, buf ); |
| |
| renderDescription( dsr, buf ); |
| |
| renderObsolete( dsr, buf ); |
| |
| prettyPrintIndent( buf ); |
| buf.append( "FORM " ).append( dsr.getForm() ); |
| prettyPrintNewLine( buf ); |
| |
| renderRuleIds( buf, dsr.getSuperRules() ); |
| |
| renderXSchemaName( dsr, buf ); |
| |
| // @todo extensions are not presently supported and skipped |
| // the extensions would go here before closing off the description |
| |
| renderClose( buf ); |
| |
| return buf.toString(); |
| } |
| |
| |
| /** |
| * NOT FULLY IMPLEMENTED! |
| * Render a NameForm as a String |
| * |
| * @param nf The NameForm to render |
| * @return The rendered String |
| */ |
| public String render( NameForm nf ) |
| { |
| StringBuilder buf = renderStartOidNamesDescObsolete( nf, "nameform" ); |
| |
| prettyPrintIndent( buf ); |
| buf.append( "OC " ).append( nf.getStructuralObjectClassOid() ); |
| prettyPrintNewLine( buf ); |
| |
| renderOids( buf, "MUST", nf.getMustAttributeTypeOids() ); |
| |
| renderOids( buf, "MAY", nf.getMayAttributeTypeOids() ); |
| |
| renderXSchemaName( nf, buf ); |
| |
| renderClose( buf ); |
| |
| return buf.toString(); |
| } |
| |
| |
| private StringBuilder renderStartOidNamesDescObsolete( SchemaObject so, String schemaObjectType ) |
| { |
| StringBuilder buf = new StringBuilder(); |
| |
| if ( style.startWithSchemaType ) |
| { |
| buf.append( schemaObjectType ).append( ' ' ); |
| } |
| |
| buf.append( "( " ).append( so.getOid() ); |
| |
| renderNames( so, buf ); |
| |
| renderDescription( so, buf ); |
| |
| renderObsolete( so, buf ); |
| return buf; |
| } |
| |
| |
| private void renderNames( SchemaObject so, StringBuilder buf ) |
| { |
| List<String> names = so.getNames(); |
| |
| if ( ( names != null ) && !names.isEmpty() ) |
| { |
| buf.append( " NAME " ); |
| renderQDescrs( buf, names ); |
| prettyPrintNewLine( buf ); |
| } |
| else |
| { |
| prettyPrintNewLine( buf ); |
| } |
| } |
| |
| |
| private void renderDescription( SchemaObject so, StringBuilder buf ) |
| { |
| if ( so.getDescription() != null ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "DESC " ); |
| renderQDString( buf, so.getDescription() ); |
| prettyPrintNewLine( buf ); |
| } |
| } |
| |
| |
| private void renderObsolete( SchemaObject so, StringBuilder buf ) |
| { |
| if ( so.isObsolete() ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "OBSOLETE" ); |
| prettyPrintNewLine( buf ); |
| } |
| } |
| |
| |
| private void prettyPrintNewLine( StringBuilder buf ) |
| { |
| if ( style.prettyPrint ) |
| { |
| buf.append( '\n' ); |
| } |
| else |
| { |
| buf.append( " " ); |
| } |
| } |
| |
| |
| private void prettyPrintIndent( StringBuilder buf ) |
| { |
| if ( style.prettyPrint ) |
| { |
| buf.append( "\t" ); |
| } |
| } |
| |
| |
| /** |
| * Renders qdescrs into a new buffer.<br> |
| * <pre> |
| * descrs ::= qdescr | '(' WSP qdescrlist WSP ')' |
| * qdescrlist ::= [ qdescr ( SP qdescr )* ] |
| * qdescr ::= SQUOTE descr SQUOTE |
| * </pre> |
| * |
| * @param buf The buffer that will contain the result. |
| * @param qdescrs the quoted description strings to render |
| * @return the string buffer the qdescrs are rendered into |
| */ |
| private StringBuilder renderQDescrs( StringBuilder buf, List<String> qdescrs ) |
| { |
| if ( ( qdescrs == null ) || qdescrs.isEmpty() ) |
| { |
| return buf; |
| } |
| |
| if ( qdescrs.size() == 1 ) |
| { |
| buf.append( '\'' ).append( qdescrs.get( 0 ) ).append( '\'' ); |
| } |
| else |
| { |
| buf.append( "( " ); |
| |
| for ( String qdescr : qdescrs ) |
| { |
| buf.append( '\'' ).append( qdescr ).append( "' " ); |
| } |
| |
| buf.append( ")" ); |
| } |
| |
| return buf; |
| } |
| |
| |
| private void renderOids( StringBuilder buf, String prefix, List<String> oids ) |
| { |
| if ( ( oids != null ) && !oids.isEmpty() ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( prefix ).append( ' ' ); |
| renderOids( buf, oids ); |
| prettyPrintNewLine( buf ); |
| } |
| } |
| |
| |
| /** |
| * Renders oids into a new buffer.<br> |
| * <pre> |
| * oids ::= oid | '(' WSP oidlist WSP ')' |
| * oidlist ::= oid ( WSP '$' WSP oid )* |
| * </pre> |
| * |
| * @param buf The resulting buffer |
| * @param oids the list of OIDs to render |
| * @return the string buffer the qdescrs are rendered into |
| */ |
| private StringBuilder renderOids( StringBuilder buf, List<String> oids ) |
| { |
| if ( oids.size() == 1 ) |
| { |
| buf.append( oids.get( 0 ) ); |
| } |
| else |
| { |
| buf.append( "( " ); |
| |
| boolean isFirst = true; |
| |
| for ( String oid : oids ) |
| { |
| if ( isFirst ) |
| { |
| isFirst = false; |
| } |
| else |
| { |
| buf.append( " $ " ); |
| } |
| |
| buf.append( oid ); |
| } |
| |
| buf.append( " )" ); |
| } |
| |
| return buf; |
| } |
| |
| |
| /** |
| * Renders QDString into a new buffer.<br> |
| * |
| * @param buf The resulting buffer |
| * @param qdString the quoted description strings to render |
| * @return the string buffer the qdescrs are rendered into |
| */ |
| private StringBuilder renderQDString( StringBuilder buf, String qdString ) |
| { |
| buf.append( '\'' ); |
| |
| for ( char c : qdString.toCharArray() ) |
| { |
| switch ( c ) |
| { |
| case 0x27: |
| buf.append( "\\27" ); |
| break; |
| |
| case 0x5C: |
| buf.append( "\\5C" ); |
| break; |
| |
| default: |
| buf.append( c ); |
| break; |
| } |
| } |
| |
| buf.append( '\'' ); |
| |
| return buf; |
| } |
| |
| |
| private StringBuilder renderRuleIds( StringBuilder buf, List<Integer> ruleIds ) |
| { |
| if ( ( ruleIds != null ) && !ruleIds.isEmpty() ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "SUP " ); |
| |
| if ( ruleIds.size() == 1 ) |
| { |
| buf.append( ruleIds.get( 0 ) ); |
| } |
| else |
| { |
| buf.append( "( " ); |
| |
| boolean isFirst = true; |
| |
| for ( Integer ruleId : ruleIds ) |
| { |
| if ( isFirst ) |
| { |
| isFirst = false; |
| } |
| else |
| { |
| buf.append( " " ); |
| } |
| |
| buf.append( ruleId ); |
| } |
| |
| buf.append( " )" ); |
| } |
| |
| prettyPrintNewLine( buf ); |
| } |
| |
| return buf; |
| } |
| |
| |
| private void renderXSchemaName( SchemaObject oc, StringBuilder buf ) |
| { |
| if ( style.printSchemaName ) |
| { |
| prettyPrintIndent( buf ); |
| buf.append( "X-SCHEMA '" ); |
| buf.append( oc.getSchemaName() ); |
| buf.append( "'" ); |
| prettyPrintNewLine( buf ); |
| } |
| } |
| |
| |
| private void renderClose( StringBuilder buf ) |
| { |
| if ( ( style.prettyPrint ) && ( buf.charAt( buf.length() - 1 ) == '\n' ) ) |
| { |
| buf.deleteCharAt( buf.length() - 1 ); |
| buf.append( " " ); |
| } |
| |
| buf.append( ")" ); |
| } |
| } |