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     */
020    package org.apache.directory.shared.ldap.schema;
021    
022    
023    import java.util.List;
024    import java.util.Map;
025    
026    
027    /**
028     * Utility class used to generate schema object specifications. Some of the
029     * latest work coming out of the LDAPBIS working body adds optional extensions
030     * to these syntaxes. Descriptions can be generated for
031     * the following objects:
032     * <ul>
033     * <li><a href="./AttributeType.html">AttributeType</a></li>
034     * <li><a href="./DITContentRule.html">DITContentRule</a></li>
035     * <li><a href="./DITContentRule.html">DITStructureRule</a></li>
036     * <li><a href="./LdapComparator.html">Syntax</a></li>
037     * <li><a href="./MatchingRule.html">MatchingRule</a></li>
038     * <li><a href="./MatchingRuleUse.html">MatchingRuleUse</a></li>
039     * <li><a href="./NameForm.html">NameForm</a></li>
040     * <li><a href="./Normalizer.html">Syntax</a></li>
041     * <li><a href="./ObjectClass.html">ObjectClass</a></li>
042     * <li><a href="./LdapSyntax.html">Syntax</a></li>
043     * <li><a href="./SyntaxChecker.html">Syntax</a></li>
044     * </ul>
045     * 
046     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
047     * @version $Rev: 896579 $
048     */
049    public class DescriptionUtils
050    {
051        /**
052         * Generates the description using the AttributeTypeDescription as defined
053         * by the syntax: 1.3.6.1.4.1.1466.115.121.1.3. Only the right hand side of
054         * the description starting at the opening parenthesis is generated: that
055         * is 'AttributeTypeDescription = ' is not generated.
056         * 
057         * <pre>
058         *  AttributeTypeDescription = &quot;(&quot; whsp
059         *     numericoid whsp                ; AttributeType identifier
060         *     [ &quot;NAME&quot; qdescrs ]             ; name used in AttributeType
061         *     [ &quot;DESC&quot; qdstring ]            ; description
062         *     [ &quot;OBSOLETE&quot; whsp ]
063         *     [ &quot;SUP&quot; woid ]                 ; derived from parent AttributeType
064         *     [ &quot;EQUALITY&quot; woid              ; Matching Rule name
065         *     [ &quot;ORDERING&quot; woid              ; Matching Rule name
066         *     [ &quot;SUBSTR&quot; woid ]              ; Matching Rule name
067         *     [ &quot;SYNTAX&quot; whsp noidlen whsp ] ; see section 4.3 RFC 2252
068         *     [ &quot;SINGLE-VALUE&quot; whsp ]        ; default multi-valued
069         *     [ &quot;COLLECTIVE&quot; whsp ]          ; default not collective
070         *     [ &quot;NO-USER-MODIFICATION&quot; whsp ]; default user modifiable
071         *     [ &quot;USAGE&quot; whsp AttributeUsage ]; default userApplications
072         *     whsp &quot;)&quot;
073         * </pre>
074         * 
075         * @param attributeType
076         *            the attributeType to generate a description for
077         * @return the AttributeTypeDescription Syntax for the attributeType in a
078         *         pretty formated string
079         */
080        public static String getDescription( AttributeType attributeType )
081        {
082            StringBuilder buf = new StringBuilder( "( " );
083            buf.append( attributeType.getOid() );
084            buf.append( '\n' );
085    
086            if ( attributeType.getNames().size() != 0 )
087            {
088                buf.append( " NAME " );
089                getQDescrs( buf, attributeType.getNames() );
090            }
091    
092            if ( attributeType.getDescription() != null )
093            {
094                buf.append( " DESC " );
095                buf.append( attributeType.getDescription() );
096                buf.append( '\n' );
097            }
098    
099            if ( attributeType.isObsolete() )
100            {
101                buf.append( " OBSOLETE\n" );
102            }
103    
104            if ( attributeType.getSuperior() != null )
105            {
106                buf.append( " SUP " );
107                buf.append( attributeType.getSuperiorName() );
108                buf.append( '\n' );
109            }
110    
111            if ( attributeType.getEquality() != null )
112            {
113                buf.append( " EQUALITY " );
114                buf.append( attributeType.getEqualityName() );
115                buf.append( '\n' );
116            }
117    
118            if ( attributeType.getOrdering() != null )
119            {
120                buf.append( " ORDERING " );
121                buf.append( attributeType.getOrderingName() );
122                buf.append( '\n' );
123            }
124    
125            if ( attributeType.getSubstring() != null )
126            {
127                buf.append( " SUBSTR " );
128                buf.append( attributeType.getSubstringName() );
129                buf.append( '\n' );
130            }
131    
132            if ( attributeType.getSyntax() != null )
133            {
134                buf.append( " SYNTAX " );
135    
136                buf.append( attributeType.getSyntaxName() );
137    
138                if ( attributeType.getSyntaxLength() > 0 )
139                {
140                    buf.append( '{' ).append( attributeType.getSyntaxLength() ).append( '}' );
141                }
142    
143                buf.append( '\n' );
144            }
145    
146            if ( attributeType.isSingleValued() )
147            {
148                buf.append( " SINGLE-VALUE\n" );
149            }
150    
151            if ( attributeType.isCollective() )
152            {
153                buf.append( " COLLECTIVE\n" );
154            }
155    
156            if ( !attributeType.isUserModifiable() )
157            {
158                buf.append( " NO-USER-MODIFICATION\n" );
159            }
160    
161            buf.append( " USAGE " );
162            buf.append( UsageEnum.render( attributeType.getUsage() ) );
163            buf.append( '\n' );
164    
165            if ( attributeType.getExtensions() != null )
166            {
167                getExtensions( buf, attributeType.getExtensions() );
168            }
169    
170            buf.append( " )\n" );
171    
172            return buf.toString();
173        }
174    
175    
176        /**
177         * Generates the ComparatorDescription for a LdapComparator. Only the right 
178         * hand side of the description starting at the opening parenthesis is 
179         * generated: that is 'ComparatorDescription = ' is not generated.
180         * 
181         * <pre>
182         * ComparatorDescription = &quot;(&quot;
183         *     numericoid                          
184         *     [&quot;DESC&quot; qdstring ]
185         *     &quot;FQCN&quot; whsp fqcn
186         *     [&quot;BYTECODE&quot; whsp base64  ]
187         *     extensions 
188         *     &quot;)&quot;
189         * </pre>
190         * 
191         * @param comparator
192         *            the Comparator to generate the description for
193         * @return the ComparatorDescription string
194         */
195        public static String getDescription( LdapComparator<?> comparator )
196        {
197            return getLoadableDescription( comparator );
198        }
199    
200    
201        /**
202         * Generates the DITContentRuleDescription for a DITContentRule as defined
203         * by the syntax: 1.3.6.1.4.1.1466.115.121.1.16. Only the right hand side of
204         * the description starting at the opening parenthesis is generated: that
205         * is 'DITContentRuleDescription = ' is not generated.
206         * 
207         * <pre>
208         *   DITContentRuleDescription = &quot;(&quot;
209         *       numericoid         ; Structural ObjectClass identifier
210         *       [ &quot;NAME&quot; qdescrs ]
211         *       [ &quot;DESC&quot; qdstring ]
212         *       [ &quot;OBSOLETE&quot; ]
213         *       [ &quot;AUX&quot; oids ]     ; Auxiliary ObjectClasses
214         *       [ &quot;MUST&quot; oids ]    ; AttributeType identifiers
215         *       [ &quot;MAY&quot; oids ]     ; AttributeType identifiers
216         *       [ &quot;NOT&quot; oids ]     ; AttributeType identifiers
217         *      &quot;)&quot;
218         * </pre>
219         * 
220         * @param dITContentRule
221         *            the DIT content rule specification
222         * @return the specification according to the DITContentRuleDescription
223         *         syntax
224         */
225        public static String getDescription( DITContentRule dITContentRule )
226        {
227            StringBuilder buf = new StringBuilder( "( " );
228            buf.append( dITContentRule.getOid() );
229            buf.append( '\n' );
230    
231            if ( dITContentRule.getNames() != null )
232            {
233                buf.append( " NAME " );
234                getQDescrs( buf, dITContentRule.getNames() );
235                buf.append( '\n' );
236            }
237    
238            if ( dITContentRule.getDescription() != null )
239            {
240                buf.append( " DESC " );
241                buf.append( dITContentRule.getDescription() );
242                buf.append( '\n' );
243            }
244    
245            if ( dITContentRule.isObsolete() )
246            {
247                buf.append( " OBSOLETE\n" );
248            }
249    
250            // print out all the auxiliary object class oids
251            List<ObjectClass> aux = dITContentRule.getAuxObjectClasses();
252    
253            if ( ( aux != null ) && ( aux.size() > 0 ) )
254            {
255                buf.append( " AUX " );
256                getQDStrings( buf, aux );
257            }
258    
259            List<AttributeType> must = dITContentRule.getMustAttributeTypes();
260    
261            if ( ( must != null ) && ( must.size() > 0 ) )
262            {
263                buf.append( " MUST " );
264                getQDStrings( buf, must );
265            }
266    
267            List<AttributeType> may = dITContentRule.getMayAttributeTypes();
268    
269            if ( ( may != null ) && ( may.size() > 0 ) )
270            {
271                buf.append( " MAY " );
272                getQDStrings( buf, may );
273            }
274    
275            List<AttributeType> not = dITContentRule.getNotAttributeTypes();
276    
277            if ( ( not != null ) && ( not.size() > 0 ) )
278            {
279                buf.append( " NOT " );
280                getQDStrings( buf, not );
281            }
282    
283            if ( dITContentRule.getExtensions() != null )
284            {
285                getExtensions( buf, dITContentRule.getExtensions() );
286            }
287    
288            buf.append( " )\n" );
289            return buf.toString();
290        }
291    
292    
293        /**
294         * Generates the DITStructureRuleDescription for a DITStructureRule as
295         * defined by the syntax: 1.3.6.1.4.1.1466.115.121.1.17. Only the right hand
296         * side of the description starting at the opening parenthesis is
297         * generated: that is 'DITStructureRuleDescription = ' is not generated.
298         * 
299         * <pre>
300         *   DITStructureRuleDescription = &quot;(&quot; whsp
301         *       ruleid                     ; rule identifier
302         *       [ SP "NAME" SP qdescrs ]   ; short names (descriptors)
303         *       [ SP "DESC" SP qdstring ]  ; description
304         *       [ SP "OBSOLETE" ]          ; not active
305         *       SP "FORM" SP oid           ; NameForm
306         *       [ SP "SUP" ruleids ]       ; superior rules
307         *       extensions WSP             ; extensions
308         *       &quot;)&quot;
309         * </pre>
310         * 
311         * @param dITStructureRule
312         *            the DITStructureRule to generate the description for
313         * @return the description in the DITStructureRuleDescription syntax
314         */
315        public static String getDescription( DITStructureRule dITStructureRule )
316        {
317            StringBuilder buf = new StringBuilder( "( " );
318            buf.append( dITStructureRule.getOid() );
319            buf.append( '\n' );
320    
321            if ( dITStructureRule.getNames() != null )
322            {
323                buf.append( " NAME " );
324                getQDescrs( buf, dITStructureRule.getNames() );
325            }
326    
327            if ( dITStructureRule.getDescription() != null )
328            {
329                buf.append( " DESC " );
330                buf.append( dITStructureRule.getDescription() );
331                buf.append( '\n' );
332            }
333    
334            if ( dITStructureRule.isObsolete() )
335            {
336                buf.append( " OBSOLETE\n" );
337            }
338    
339            buf.append( " FORM " );
340            buf.append( dITStructureRule.getForm() );
341            buf.append( '\n' );
342    
343            // TODO : Shouldn't we get the ruleId OID ? 
344            List<Integer> sups = dITStructureRule.getSuperRules();
345    
346            if ( ( sups != null ) && ( sups.size() > 0 ) )
347            {
348                buf.append( " SUP\n" );
349    
350                if ( sups.size() == 1 )
351                {
352                    buf.append( sups.get( 0 ) );
353                }
354                else
355                {
356                    boolean isFirst = true;
357                    buf.append( "( " );
358    
359                    for ( int sup : sups )
360                    {
361                        if ( isFirst )
362                        {
363                            isFirst = false;
364                        }
365                        else
366                        {
367                            buf.append( " " );
368                        }
369    
370                        buf.append( sup );
371                    }
372    
373                    buf.append( " )" );
374                }
375    
376                buf.append( '\n' );
377            }
378    
379            buf.append( " )\n" );
380    
381            return buf.toString();
382        }
383    
384    
385        /**
386         * Generates the MatchingRuleDescription for a MatchingRule as defined by
387         * the syntax: 1.3.6.1.4.1.1466.115.121.1.30. Only the right hand side of
388         * the description starting at the opening parenthesis is generated: that
389         * is 'MatchingRuleDescription = ' is not generated.
390         * 
391         * <pre>
392         *  MatchingRuleDescription = &quot;(&quot; whsp
393         *     numericoid whsp      ; MatchingRule object identifier
394         *     [ &quot;NAME&quot; qdescrs ]
395         *     [ &quot;DESC&quot; qdstring ]
396         *     [ &quot;OBSOLETE&quot; whsp ]
397         *     &quot;SYNTAX&quot; numericoid
398         *  whsp &quot;)&quot;
399         * </pre>
400         * 
401         * @param matchingRule
402         *            the MatchingRule to generate the description for
403         * @return the MatchingRuleDescription string
404         */
405        public static String getDescription( MatchingRule matchingRule )
406        {
407            StringBuilder buf = new StringBuilder( "( " );
408            buf.append( matchingRule.getOid() );
409            buf.append( '\n' );
410    
411            if ( matchingRule.getNames() != null )
412            {
413                buf.append( " NAME " );
414                getQDescrs( buf, matchingRule.getNames() );
415            }
416    
417            if ( matchingRule.getDescription() != null )
418            {
419                buf.append( " DESC " );
420                buf.append( matchingRule.getDescription() );
421                buf.append( '\n' );
422            }
423    
424            if ( matchingRule.isObsolete() )
425            {
426                buf.append( " OBSOLETE\n" );
427            }
428    
429            buf.append( " SYNTAX " );
430            buf.append( matchingRule.getSyntaxOid() );
431            buf.append( '\n' );
432    
433            if ( matchingRule.getExtensions() != null )
434            {
435                getExtensions( buf, matchingRule.getExtensions() );
436            }
437    
438            buf.append( " ) " );
439            return buf.toString();
440        }
441    
442    
443        /**
444         * Generates the MatchingRuleUseDescription for a MatchingRuleUse as defined
445         * by the syntax: 1.3.6.1.4.1.1466.115.121.1.31. Only the right hand side of
446         * the description starting at the opening parenthesis is generated: that
447         * is 'MatchingRuleUseDescription = ' is not generated.
448         * 
449         * <pre>
450         *      MatchingRuleUseDescription = LPAREN WSP
451         *          numericoid                ; object identifier
452         *          [ SP &quot;NAME&quot; SP qdescrs ]  ; short names (descriptors)
453         *          [ SP &quot;DESC&quot; SP qdstring ] ; description
454         *          [ SP &quot;OBSOLETE&quot; ]         ; not active
455         *          SP &quot;APPLIES&quot; SP oids      ; attribute types
456         *          extensions WSP RPAREN     ; extensions
457         *  
458         *    where:
459         *      [numericoid] is the object identifier of the matching rule
460         *          associated with this matching rule use description;
461         *      NAME [qdescrs] are short names (descriptors) identifying this
462         *          matching rule use;
463         *      DESC [qdstring] is a short descriptive string;
464         *      OBSOLETE indicates this matching rule use is not active;
465         *      APPLIES provides a list of attribute types the matching rule applies
466         *          to; and
467         *      [extensions] describe extensions.
468         * </pre>
469         * 
470         * @param matchingRuleUse The matching rule from which we want to generate
471         *  a MatchingRuleUseDescription.
472         * @return The generated MatchingRuleUseDescription
473         */
474        public static String getDescription( MatchingRuleUse matchingRuleUse )
475        {
476            StringBuilder buf = new StringBuilder( "( " );
477            buf.append( matchingRuleUse.getOid() );
478            buf.append( '\n' );
479    
480            buf.append( " NAME " );
481            getQDescrs( buf, matchingRuleUse.getNames() );
482    
483            if ( matchingRuleUse.getDescription() != null )
484            {
485                buf.append( " DESC " );
486                buf.append( matchingRuleUse.getDescription() );
487                buf.append( '\n' );
488            }
489    
490            if ( matchingRuleUse.isObsolete() )
491            {
492                buf.append( " OBSOLETE\n" );
493            }
494    
495            buf.append( " APPLIES " );
496            List<AttributeType> attributeTypes = matchingRuleUse.getApplicableAttributes();
497    
498            if ( attributeTypes.size() == 1 )
499            {
500                buf.append( attributeTypes.get( 0 ).getOid() );
501            }
502            else
503            // for list of oids we need a parenthesis
504            {
505                buf.append( "( " );
506    
507                boolean isFirst = true;
508    
509                for ( AttributeType attributeType : attributeTypes )
510                {
511                    if ( isFirst )
512                    {
513                        isFirst = false;
514                    }
515                    else
516                    {
517                        buf.append( " $ " );
518                    }
519    
520                    buf.append( attributeType );
521                }
522    
523                buf.append( " ) " );
524            }
525    
526            if ( matchingRuleUse.getExtensions() != null )
527            {
528                getExtensions( buf, matchingRuleUse.getExtensions() );
529            }
530    
531            buf.append( " )\n" );
532    
533            return buf.toString();
534        }
535    
536    
537        /**
538         * Generates the NameFormDescription for a NameForm as defined by the
539         * syntax: 1.3.6.1.4.1.1466.115.121.1.35. Only the right hand side of the
540         * description starting at the opening parenthesis is generated: that is
541         * 'NameFormDescription = ' is not generated.
542         * 
543         * <pre>
544         *  NameFormDescription = &quot;(&quot; whsp
545         *      numericoid whsp               ; NameForm identifier
546         *      [ &quot;NAME&quot; qdescrs ]
547         *      [ &quot;DESC&quot; qdstring ]
548         *      [ &quot;OBSOLETE&quot; whsp ]
549         *      &quot;OC&quot; woid                     ; Structural ObjectClass
550         *      &quot;MUST&quot; oids                   ; AttributeTypes
551         *      [ &quot;MAY&quot; oids ]                ; AttributeTypes
552         *  whsp &quot;)&quot;
553         * </pre>
554         * 
555         * @param nameForm
556         *            the NameForm to generate the description for
557         * @return the NameFormDescription string
558         */
559        public static String getDescription( NameForm nameForm )
560        {
561            StringBuilder buf = new StringBuilder( "( " );
562            buf.append( nameForm.getOid() );
563            buf.append( '\n' );
564    
565            if ( nameForm.getNames() != null )
566            {
567                buf.append( " NAME " );
568                getQDescrs( buf, nameForm.getNames() );
569            }
570    
571            if ( nameForm.getDescription() != null )
572            {
573                buf.append( " DESC " );
574                buf.append( nameForm.getDescription() );
575                buf.append( '\n' );
576            }
577    
578            if ( nameForm.isObsolete() )
579            {
580                buf.append( " OBSOLETE\n" );
581            }
582    
583            buf.append( " OC " );
584            buf.append( nameForm.getStructuralObjectClassOid() );
585            buf.append( '\n' );
586    
587            buf.append( " MUST\n" );
588            List<AttributeType> must = nameForm.getMustAttributeTypes();
589    
590            getQDStrings( buf, must );
591    
592            List<AttributeType> may = nameForm.getMayAttributeTypes();
593    
594            if ( ( may != null ) && ( may.size() > 0 ) )
595            {
596                buf.append( " MAY\n" );
597                getQDStrings( buf, may );
598            }
599    
600            if ( nameForm.getExtensions() != null )
601            {
602                getExtensions( buf, nameForm.getExtensions() );
603            }
604    
605            buf.append( " )\n" );
606            return buf.toString();
607        }
608    
609    
610        /**
611         * Generates the NormalizerDescription for a Normalizer. Only the right 
612         * hand side of the description starting at the opening parenthesis is 
613         * generated: that is 'NormalizerDescription = ' is not generated.
614         * 
615         * <pre>
616         * NormalizerDescription = &quot;(&quot;
617         *     numericoid                          
618         *     [&quot;DESC&quot; qdstring ]
619         *     &quot;FQCN&quot; whsp fqcn
620         *     [&quot;BYTECODE&quot; whsp base64  ]
621         *     extensions 
622         *     &quot;)&quot;
623         * </pre>
624         * 
625         * @param normalizer
626         *            the Normalizer to generate the description for
627         * @return the NormalizerDescription string
628         */
629        public static String getDescription( Normalizer normalizer )
630        {
631            return getLoadableDescription( normalizer );
632        }
633    
634    
635        /**
636         * Generates the ObjectClassDescription for an ObjectClass as defined by the
637         * syntax: 1.3.6.1.4.1.1466.115.121.1.37. Only the right hand side of the
638         * description starting at the opening parenthesis is generated: that is
639         * 'ObjectClassDescription = ' is not generated.
640         * 
641         * <pre>
642         *  ObjectClassDescription = &quot;(&quot; whsp
643         *      numericoid whsp     ; ObjectClass identifier
644         *      [ &quot;NAME&quot; qdescrs ]
645         *      [ &quot;DESC&quot; qdstring ]
646         *      [ &quot;OBSOLETE&quot; whsp ]
647         *      [ &quot;SUP&quot; oids ]      ; Superior ObjectClasses
648         *      [ ( &quot;ABSTRACT&quot; / &quot;STRUCTURAL&quot; / &quot;AUXILIARY&quot; ) whsp ]
649         *                          ; default structural
650         *      [ &quot;MUST&quot; oids ]     ; AttributeTypes
651         *      [ &quot;MAY&quot; oids ]      ; AttributeTypes
652         *  whsp &quot;)&quot;
653         * </pre>
654         * 
655         * @param objectClass
656         *            the ObjectClass to generate a description for
657         * @return the description in the ObjectClassDescription syntax
658         */
659        public static String getDescription( ObjectClass objectClass )
660        {
661            StringBuilder buf = new StringBuilder( "( " );
662            buf.append( objectClass.getOid() );
663            buf.append( '\n' );
664    
665            if ( ( objectClass.getNames() != null ) && ( objectClass.getNames().size() != 0 ) )
666            {
667                buf.append( " NAME " );
668                getQDescrs( buf, objectClass.getNames() );
669            }
670    
671            if ( objectClass.getDescription() != null )
672            {
673                buf.append( " DESC " );
674                buf.append( objectClass.getDescription() );
675                buf.append( '\n' );
676            }
677    
678            if ( objectClass.isObsolete() )
679            {
680                buf.append( " OBSOLETE\n" );
681            }
682    
683            List<ObjectClass> sups = objectClass.getSuperiors();
684    
685            if ( ( sups != null ) && ( sups.size() > 0 ) )
686            {
687                buf.append( " SUP " );
688                getQDStrings( buf, sups );
689            }
690    
691            if ( objectClass.getType() != null )
692            {
693                buf.append( ' ' );
694                buf.append( objectClass.getType() );
695                buf.append( '\n' );
696            }
697    
698            List<AttributeType> must = objectClass.getMustAttributeTypes();
699    
700            if ( ( must != null ) && ( must.size() > 0 ) )
701            {
702                buf.append( " MUST " );
703                getQDStrings( buf, must );
704            }
705    
706            List<AttributeType> may = objectClass.getMayAttributeTypes();
707    
708            if ( ( may != null ) && ( may.size() > 0 ) )
709            {
710                buf.append( " MAY " );
711                getQDStrings( buf, may );
712            }
713    
714            if ( objectClass.getExtensions() != null )
715            {
716                getExtensions( buf, objectClass.getExtensions() );
717            }
718    
719            buf.append( " )\n" );
720    
721            return buf.toString();
722        }
723    
724    
725        /**
726         * Generates the SyntaxDescription for a Syntax as defined by the syntax:
727         * 1.3.6.1.4.1.1466.115.121.1.54. Only the right hand side of the
728         * description starting at the opening parenthesis is generated: that is
729         * 'SyntaxDescription = ' is not generated.
730         * 
731         * <pre>
732         *  SyntaxDescription = &quot;(&quot; whsp
733         *      numericoid whsp
734         *      [ &quot;DESC&quot; qdstring ]
735         *      [ extensions ]
736         *      whsp &quot;)&quot;
737         * </pre>
738         * 
739         * @param syntax
740         *            the Syntax to generate a description for
741         * @return the description in the SyntaxDescription syntax
742         */
743        public static String getDescription( LdapSyntax syntax )
744        {
745            StringBuilder buf = new StringBuilder( "( " );
746            buf.append( syntax.getOid() );
747            buf.append( '\n' );
748    
749            if ( syntax.getDescription() != null )
750            {
751                buf.append( " DESC " );
752                buf.append( syntax.getDescription() );
753                buf.append( '\n' );
754            }
755    
756            if ( syntax.getExtensions() != null )
757            {
758                getExtensions( buf, syntax.getExtensions() );
759            }
760    
761            buf.append( " )" );
762            return buf.toString();
763        }
764    
765    
766        /**
767         * Generates the SyntaxCheckerDescription for a SyntaxChecker. Only the right 
768         * hand side of the description starting at the opening parenthesis is 
769         * generated: that is 'SyntaxCheckerDescription = ' is not generated.
770         * 
771         * <pre>
772         * SyntaxCheckerDescription = &quot;(&quot;
773         *     numericoid                          
774         *     [&quot;DESC&quot; qdstring ]
775         *     &quot;FQCN&quot; whsp fqcn
776         *     [&quot;BYTECODE&quot; whsp base64  ]
777         *     extensions 
778         *     &quot;)&quot;
779         * </pre>
780         * 
781         * @param syntaxChecker
782         *            the SyntaxChecker to generate the description for
783         * @return the SyntaxCheckerDescription string
784         */
785        public static String getDescription( SyntaxChecker syntaxChecker )
786        {
787            return getLoadableDescription( syntaxChecker );
788        }
789    
790    
791        private static void getExtensions( StringBuilder sb, Map<String, List<String>> extensions )
792        {
793            for ( String key : extensions.keySet() )
794            {
795                sb.append( key ).append( " " );
796    
797                List<String> values = extensions.get( key );
798    
799                if ( ( values != null ) && ( values.size() != 0 ) )
800                {
801                    if ( values.size() == 1 )
802                    {
803                        sb.append( values.get( 0 ) );
804                    }
805                    else
806                    {
807                        boolean isFirst = true;
808                        sb.append( "( " );
809    
810                        for ( String value : values )
811                        {
812                            if ( isFirst )
813                            {
814                                isFirst = false;
815                            }
816                            else
817                            {
818                                sb.append( " " );
819                            }
820    
821                            sb.append( value );
822                        }
823    
824                        sb.append( " )" );
825                    }
826                }
827    
828                sb.append( '\n' );
829            }
830        }
831    
832    
833        private static void getQDStrings( StringBuilder sb, List<? extends SchemaObject> schemaObjects )
834        {
835            if ( ( schemaObjects != null ) && ( schemaObjects.size() != 0 ) )
836            {
837                if ( schemaObjects.size() == 1 )
838                {
839                    sb.append( '\'' ).append( schemaObjects.get( 0 ).getName() ).append( '\'' );
840                }
841                else
842                {
843                    boolean isFirst = true;
844                    sb.append( "( " );
845    
846                    for ( SchemaObject schemaObject : schemaObjects )
847                    {
848                        if ( isFirst )
849                        {
850                            isFirst = false;
851                        }
852                        else
853                        {
854                            sb.append( " " );
855                        }
856    
857                        sb.append( '\'' ).append( schemaObject.getName() ).append( '\'' );
858                    }
859    
860                    sb.append( " )" );
861                }
862            }
863    
864            sb.append( '\n' );
865        }
866    
867    
868        private static void getQDescrs( StringBuilder sb, List<String> names )
869        {
870            if ( ( names != null ) && ( names.size() != 0 ) )
871            {
872                if ( names.size() == 1 )
873                {
874                    sb.append( '\'' ).append( names.get( 0 ) ).append( '\'' );
875                }
876                else
877                {
878                    boolean isFirst = true;
879                    sb.append( "( " );
880    
881                    for ( String name : names )
882                    {
883                        if ( isFirst )
884                        {
885                            isFirst = false;
886                        }
887                        else
888                        {
889                            sb.append( " " );
890                        }
891    
892                        sb.append( '\'' ).append( name ).append( '\'' );
893                    }
894    
895                    sb.append( " )" );
896                }
897            }
898    
899            sb.append( '\n' );
900        }
901    
902    
903        /**
904         * Generate the description for Comparators, Normalizers and SyntaxCheckers.
905         */
906        private static String getLoadableDescription( LoadableSchemaObject schemaObject )
907        {
908            StringBuilder buf = new StringBuilder( "( " );
909            buf.append( schemaObject.getOid() );
910            buf.append( '\n' );
911    
912            if ( schemaObject.getDescription() != null )
913            {
914                buf.append( " DESC " );
915                buf.append( schemaObject.getDescription() );
916                buf.append( '\n' );
917            }
918    
919            if ( schemaObject.getFqcn() != null )
920            {
921                buf.append( " FQCN " );
922                buf.append( schemaObject.getFqcn() );
923                buf.append( '\n' );
924            }
925    
926            if ( schemaObject.getBytecode() != null )
927            {
928                buf.append( " BYTECODE " );
929    
930                // We will dump only the 16 first bytes
931                if ( schemaObject.getBytecode().length() > 16 )
932                {
933                    buf.append( schemaObject.getBytecode().substring( 0, 16 ) );
934                }
935                else
936                {
937                    buf.append( schemaObject.getBytecode() );
938                }
939    
940                buf.append( '\n' );
941            }
942    
943            if ( schemaObject.getExtensions() != null )
944            {
945                getExtensions( buf, schemaObject.getExtensions() );
946            }
947    
948            buf.append( " ) " );
949    
950            return buf.toString();
951        }
952    }