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 javax.naming.NamingException;
024    
025    
026    /**
027     * Utility class used to generate schema object specifications. Some of the
028     * latest work coming out of the LDAPBIS working body adds optional extensions
029     * to these syntaxes. We have not yet added extension support to these functions
030     * or the schema interfaces in this package. 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="./MatchingRule.html">MatchingRule</a></li>
036     * <li><a href="./MatchingRuleUse.html">MatchingRuleUse</a></li>
037     * <li><a href="./NameForm.html">NameForm</a></li>
038     * <li><a href="./ObjectClass.html">ObjectClass</a></li>
039     * <li><a href="./DITStructureRule.html">DITStructureRule</a></li>
040     * <li><a href="./Syntax.html">Syntax</a></li>
041     * </ul>
042     * 
043     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
044     * @version $Rev: 664290 $
045     */
046    public class DescriptionUtils
047    {
048        /**
049         * Generates the description using the AttributeTypeDescription as defined
050         * by the syntax: 1.3.6.1.4.1.1466.115.121.1.3. Only the right hand side of
051         * the description starting at the openning parenthesis is generated: that
052         * is 'AttributeTypeDescription = ' is not generated.
053         * 
054         * <pre>
055         *  AttributeTypeDescription = &quot;(&quot; whsp
056         *     numericoid whsp                ; AttributeType identifier
057         *     [ &quot;NAME&quot; qdescrs ]             ; name used in AttributeType
058         *     [ &quot;DESC&quot; qdstring ]            ; description
059         *     [ &quot;OBSOLETE&quot; whsp ]
060         *     [ &quot;SUP&quot; woid ]                 ; derived from parent AttributeType
061         *     [ &quot;EQUALITY&quot; woid              ; Matching Rule name
062         *     [ &quot;ORDERING&quot; woid              ; Matching Rule name
063         *     [ &quot;SUBSTR&quot; woid ]              ; Matching Rule name
064         *     [ &quot;SYNTAX&quot; whsp noidlen whsp ] ; see section 4.3 RFC 2252
065         *     [ &quot;SINGLE-VALUE&quot; whsp ]        ; default multi-valued
066         *     [ &quot;COLLECTIVE&quot; whsp ]          ; default not collective
067         *     [ &quot;NO-USER-MODIFICATION&quot; whsp ]; default user modifiable
068         *     [ &quot;USAGE&quot; whsp AttributeUsage ]; default userApplications
069         *     whsp &quot;)&quot;
070         * </pre>
071         * 
072         * @param attributeType
073         *            the attributeType to generate a description for
074         * @return the AttributeTypeDescription Syntax for the attributeType in a
075         *         pretty formated string
076         * @throws NamingException If an error is raised while accessing some of the attributeType
077         * data 
078         */
079        public static String getDescription( AttributeType attributeType ) throws NamingException
080        {
081            StringBuffer buf = new StringBuffer( "( " );
082            buf.append( attributeType.getOid() );
083            buf.append( '\n' );
084    
085            buf.append( "NAME " );
086            buf.append( attributeType.getName() );
087            buf.append( '\n' );
088    
089            if ( attributeType.getDescription() != null )
090            {
091                buf.append( "DESC " );
092                buf.append( attributeType.getDescription() );
093                buf.append( '\n' );
094            }
095    
096            if ( attributeType.isObsolete() )
097            {
098                buf.append( "OBSOLETE" );
099                buf.append( '\n' );
100            }
101    
102            buf.append( attributeType.getSuperior().getOid() );
103    
104            if ( attributeType.getEquality() != null )
105            {
106                buf.append( "EQUALITY " );
107                buf.append( attributeType.getEquality().getOid() );
108                buf.append( '\n' );
109            }
110    
111            if ( attributeType.getOrdering() != null )
112            {
113                buf.append( "ORDERING " );
114                buf.append( attributeType.getOrdering().getOid() );
115                buf.append( '\n' );
116            }
117    
118            if ( attributeType.getSubstr() != null )
119            {
120                buf.append( "SUBSTR " );
121                buf.append( attributeType.getSubstr().getOid() );
122                buf.append( '\n' );
123            }
124    
125            buf.append( "SYNTAX " );
126            buf.append( attributeType.getSyntax().getOid() );
127            buf.append( '\n' );
128    
129            if ( attributeType.isSingleValue() )
130            {
131                buf.append( "SINGLE-VALUE" );
132                buf.append( '\n' );
133            }
134    
135            if ( attributeType.isCollective() )
136            {
137                buf.append( "COLLECTIVE" );
138                buf.append( '\n' );
139            }
140    
141            if ( attributeType.isCanUserModify() )
142            {
143                buf.append( "NO-USER-MODIFICATION" );
144                buf.append( '\n' );
145            }
146    
147            buf.append( "USAGE " );
148            buf.append( UsageEnum.render( attributeType.getUsage() ) );
149            buf.append( " ) " );
150    
151            return buf.toString();
152        }
153    
154    
155        /**
156         * Generates the DITContentRuleDescription for a DITContentRule as defined
157         * by the syntax: 1.3.6.1.4.1.1466.115.121.1.16. Only the right hand side of
158         * the description starting at the openning parenthesis is generated: that
159         * is 'DITContentRuleDescription = ' is not generated.
160         * 
161         * <pre>
162         *   DITContentRuleDescription = &quot;(&quot;
163         *       numericoid         ; Structural ObjectClass identifier
164         *       [ &quot;NAME&quot; qdescrs ]
165         *       [ &quot;DESC&quot; qdstring ]
166         *       [ &quot;OBSOLETE&quot; ]
167         *       [ &quot;AUX&quot; oids ]     ; Auxiliary ObjectClasses
168         *       [ &quot;MUST&quot; oids ]    ; AttributeType identifiers
169         *       [ &quot;MAY&quot; oids ]     ; AttributeType identifiers
170         *       [ &quot;NOT&quot; oids ]     ; AttributeType identifiers
171         *      &quot;)&quot;
172         * </pre>
173         * 
174         * @param dITContentRule
175         *            the DIT content rule specification
176         * @return the specification according to the DITContentRuleDescription
177         *         syntax
178         * @throws NamingException If an error is raised while accessing some of the dITConentRule
179         * data 
180         */
181        public static String getDescription( DITContentRule dITContentRule ) throws NamingException
182        {
183            StringBuffer buf = new StringBuffer( "( " );
184            buf.append( dITContentRule.getOid() );
185            buf.append( '\n' );
186    
187            buf.append( "NAME " );
188            buf.append( dITContentRule.getName() );
189            buf.append( '\n' );
190    
191            if ( dITContentRule.getDescription() != null )
192            {
193                buf.append( "DESC " );
194                buf.append( dITContentRule.getDescription() );
195                buf.append( '\n' );
196            }
197    
198            if ( dITContentRule.isObsolete() )
199            {
200                buf.append( "OBSOLETE" );
201                buf.append( '\n' );
202            }
203    
204            // print out all the auxiliary object class oids
205            ObjectClass[] aux = dITContentRule.getAuxObjectClasses();
206            
207            if ( aux != null && aux.length > 0 )
208            {
209                buf.append( "AUX\n" );
210                
211                for ( ObjectClass objectClass: aux )
212                {
213                    buf.append( '\t' );
214                    buf.append( objectClass.getOid() );
215                    buf.append( '\n' );
216                }
217            }
218    
219            AttributeType[] must = dITContentRule.getMustNames();
220            
221            if ( must != null && must.length > 0 )
222            {
223                buf.append( "MUST\n" );
224                
225                for ( AttributeType attributeType:must )
226                {
227                    buf.append( '\t' );
228                    buf.append( attributeType.getOid() );
229                    buf.append( '\n' );
230                }
231            }
232    
233            AttributeType[] may = dITContentRule.getMayNames();
234            
235            if ( may != null && may.length > 0 )
236            {
237                buf.append( "MAY\n" );
238                
239                for ( AttributeType attributeType:may )
240                {
241                    buf.append( '\t' );
242                    buf.append( attributeType.getOid() );
243                    buf.append( '\n' );
244                }
245            }
246    
247            AttributeType[] not = dITContentRule.getNotNames();
248            
249            if ( not != null && not.length > 0 )
250            {
251                buf.append( "NOT\n" );
252                
253                for ( AttributeType attributeType:not )
254                {
255                    buf.append( '\t' );
256                    buf.append( attributeType.getOid() );
257                    buf.append( '\n' );
258                }
259            }
260    
261            buf.append( " )" );
262            return buf.toString();
263        }
264    
265    
266        /**
267         * Generates the MatchingRuleDescription for a MatchingRule as defined by
268         * the syntax: 1.3.6.1.4.1.1466.115.121.1.30. Only the right hand side of
269         * the description starting at the openning parenthesis is generated: that
270         * is 'MatchingRuleDescription = ' is not generated.
271         * 
272         * <pre>
273         *  MatchingRuleDescription = &quot;(&quot; whsp
274         *     numericoid whsp      ; MatchingRule object identifier
275         *     [ &quot;NAME&quot; qdescrs ]
276         *     [ &quot;DESC&quot; qdstring ]
277         *     [ &quot;OBSOLETE&quot; whsp ]
278         *     &quot;SYNTAX&quot; numericoid
279         *  whsp &quot;)&quot;
280         * </pre>
281         * 
282         * @param matchingRule
283         *            the MatchingRule to generate the description for
284         * @return the MatchingRuleDescription string
285         * @throws NamingException If an error is raised while accessing some of the matchingRule
286         * data 
287         */
288        public static String getDescription( MatchingRule matchingRule ) throws NamingException
289        {
290            StringBuffer buf = new StringBuffer( "( " );
291            buf.append( matchingRule.getOid() );
292            buf.append( '\n' );
293    
294            buf.append( "NAME " );
295            buf.append( matchingRule.getName() );
296            buf.append( '\n' );
297    
298            if ( matchingRule.getDescription() != null )
299            {
300                buf.append( "DESC " );
301                buf.append( matchingRule.getDescription() );
302                buf.append( '\n' );
303            }
304    
305            if ( matchingRule.isObsolete() )
306            {
307                buf.append( "OBSOLETE" );
308                buf.append( '\n' );
309            }
310    
311            buf.append( "SYNTAX " );
312            buf.append( matchingRule.getSyntax().getOid() );
313            buf.append( " ) " );
314            return buf.toString();
315        }
316    
317    
318        /**
319         * Generates the MatchingRuleUseDescription for a MatchingRuleUse as defined
320         * by the syntax: 1.3.6.1.4.1.1466.115.121.1.31. Only the right hand side of
321         * the description starting at the openning parenthesis is generated: that
322         * is 'MatchingRuleUseDescription = ' is not generated.
323         * 
324         * <pre>
325         *      MatchingRuleUseDescription = LPAREN WSP
326         *          numericoid                ; object identifier
327         *          [ SP &quot;NAME&quot; SP qdescrs ]  ; short names (descriptors)
328         *          [ SP &quot;DESC&quot; SP qdstring ] ; description
329         *          [ SP &quot;OBSOLETE&quot; ]         ; not active
330         *          SP &quot;APPLIES&quot; SP oids      ; attribute types
331         *          extensions WSP RPAREN     ; extensions
332         *  
333         *    where:
334         *      [numericoid] is the object identifier of the matching rule
335         *          associated with this matching rule use description;
336         *      NAME [qdescrs] are short names (descriptors) identifying this
337         *          matching rule use;
338         *      DESC [qdstring] is a short descriptive string;
339         *      OBSOLETE indicates this matching rule use is not active;
340         *      APPLIES provides a list of attribute types the matching rule applies
341         *          to; and
342         *      [extensions] describe extensions.
343         * </pre>
344         * 
345         * @param matchingRuleUse The matching rule from which we want to generate
346         *  a MatchingRuleUseDescription.
347         * @return The generated MatchingRuleUseDescription
348         * @throws NamingException If an error is raised while accessing some of the matchingRuleUse
349         * data 
350         */
351        public static String getDescription( MatchingRuleUse matchingRuleUse ) throws NamingException
352        {
353            StringBuffer buf = new StringBuffer( "( " );
354            buf.append( matchingRuleUse.getMatchingRule().getOid() );
355            buf.append( '\n' );
356    
357            buf.append( "NAME " );
358            buf.append( matchingRuleUse.getName() );
359            buf.append( '\n' );
360    
361            if ( matchingRuleUse.getDescription() != null )
362            {
363                buf.append( "DESC " );
364                buf.append( matchingRuleUse.getDescription() );
365                buf.append( '\n' );
366            }
367    
368            if ( matchingRuleUse.isObsolete() )
369            {
370                buf.append( "OBSOLETE" );
371                buf.append( '\n' );
372            }
373    
374            buf.append( "APPLIES " );
375            AttributeType[] attributeTypes = matchingRuleUse.getApplicableAttributes();
376            if ( attributeTypes.length == 1 )
377            {
378                buf.append( attributeTypes[0].getOid() );
379            }
380            else
381            // for list of oids we need a parenthesis
382            {
383                buf.append( "( " );
384                buf.append( attributeTypes[0] );
385                for ( int ii = 1; ii < attributeTypes.length; ii++ )
386                {
387                    buf.append( " $ " );
388                    buf.append( attributeTypes[ii] );
389                }
390                buf.append( " ) " );
391            }
392    
393            buf.append( '\n' );
394            return buf.toString();
395        }
396    
397    
398        /**
399         * Generates the NameFormDescription for a NameForm as defined by the
400         * syntax: 1.3.6.1.4.1.1466.115.121.1.35. Only the right hand side of the
401         * description starting at the openning parenthesis is generated: that is
402         * 'NameFormDescription = ' is not generated.
403         * 
404         * <pre>
405         *  NameFormDescription = &quot;(&quot; whsp
406         *      numericoid whsp               ; NameForm identifier
407         *      [ &quot;NAME&quot; qdescrs ]
408         *      [ &quot;DESC&quot; qdstring ]
409         *      [ &quot;OBSOLETE&quot; whsp ]
410         *      &quot;OC&quot; woid                     ; Structural ObjectClass
411         *      &quot;MUST&quot; oids                   ; AttributeTypes
412         *      [ &quot;MAY&quot; oids ]                ; AttributeTypes
413         *  whsp &quot;)&quot;
414         * </pre>
415         * 
416         * @param nameForm
417         *            the NameForm to generate the description for
418         * @return the NameFormDescription string
419         * @throws NamingException If an error is raised while accessing some of the nameForm
420         * data 
421         */
422        public static String getDescription( NameForm nameForm ) throws NamingException
423        {
424            StringBuffer buf = new StringBuffer( "( " );
425            buf.append( nameForm.getOid() );
426            buf.append( '\n' );
427    
428            buf.append( "NAME " );
429            buf.append( nameForm.getName() );
430            buf.append( '\n' );
431    
432            if ( nameForm.getDescription() != null )
433            {
434                buf.append( "DESC " );
435                buf.append( nameForm.getDescription() );
436                buf.append( '\n' );
437            }
438    
439            if ( nameForm.isObsolete() )
440            {
441                buf.append( "OBSOLETE" );
442                buf.append( '\n' );
443            }
444    
445            buf.append( "OC " );
446            buf.append( nameForm.getObjectClass().getOid() );
447            buf.append( '\n' );
448    
449            buf.append( "MUST\n" );
450            AttributeType[] must = nameForm.getMustUse();
451            
452            for ( AttributeType attributeType:must )
453            {
454                buf.append( '\t' );
455                buf.append( attributeType.getOid() );
456                buf.append( '\n' );
457            }
458    
459            AttributeType[] may = nameForm.getMayUse();
460    
461            if ( may != null && may.length > 0 )
462            {
463                buf.append( "MAY\n" );
464            
465                for ( AttributeType attributeType:may )
466                {
467                    buf.append( '\t' );
468                    buf.append( attributeType.getOid() );
469                    buf.append( '\n' );
470                }
471            }
472    
473            buf.append( " )" );
474            return buf.toString();
475        }
476    
477    
478        /**
479         * Generates the ObjectClassDescription for an ObjectClass as defined by the
480         * syntax: 1.3.6.1.4.1.1466.115.121.1.37. Only the right hand side of the
481         * description starting at the openning parenthesis is generated: that is
482         * 'ObjectClassDescription = ' is not generated.
483         * 
484         * <pre>
485         *  ObjectClassDescription = &quot;(&quot; whsp
486         *      numericoid whsp     ; ObjectClass identifier
487         *      [ &quot;NAME&quot; qdescrs ]
488         *      [ &quot;DESC&quot; qdstring ]
489         *      [ &quot;OBSOLETE&quot; whsp ]
490         *      [ &quot;SUP&quot; oids ]      ; Superior ObjectClasses
491         *      [ ( &quot;ABSTRACT&quot; / &quot;STRUCTURAL&quot; / &quot;AUXILIARY&quot; ) whsp ]
492         *                          ; default structural
493         *      [ &quot;MUST&quot; oids ]     ; AttributeTypes
494         *      [ &quot;MAY&quot; oids ]      ; AttributeTypes
495         *  whsp &quot;)&quot;
496         * </pre>
497         * 
498         * @param objectClass
499         *            the ObjectClass to generate a description for
500         * @return the description in the ObjectClassDescription syntax
501         * @throws NamingException If an error is raised while accessing some of the objectClass
502         * data 
503         */
504        public static String getDescription( ObjectClass objectClass ) throws NamingException
505        {
506            StringBuffer buf = new StringBuffer( "( " );
507            buf.append( objectClass.getOid() );
508            buf.append( '\n' );
509    
510            buf.append( "NAME " );
511            buf.append( objectClass.getName() );
512            buf.append( '\n' );
513    
514            if ( objectClass.getDescription() != null )
515            {
516                buf.append( "DESC " );
517                buf.append( objectClass.getDescription() );
518                buf.append( '\n' );
519            }
520    
521            if ( objectClass.isObsolete() )
522            {
523                buf.append( "OBSOLETE" );
524                buf.append( '\n' );
525            }
526    
527            ObjectClass[] sups = objectClass.getSuperClasses();
528    
529            if ( sups != null && sups.length > 0 )
530            {
531                buf.append( "SUP\n" );
532                
533                for ( ObjectClass sup:sups )
534                {
535                    buf.append( '\t' );
536                    buf.append( sup.getOid() );
537                    buf.append( '\n' );
538                }
539            }
540    
541            if ( objectClass.getType() != null )
542            {
543                buf.append( objectClass.getType() );
544                buf.append( '\n' );
545            }
546    
547            AttributeType[] must = objectClass.getMustList();
548            if ( must != null && must.length > 0 )
549            {
550                buf.append( "MUST\n" );
551                
552                for ( AttributeType attributeType:must )
553                {
554                    buf.append( '\t' );
555                    buf.append( attributeType.getOid() );
556                    buf.append( '\n' );
557                }
558            }
559    
560            AttributeType[] may = objectClass.getMayList();
561            
562            if ( may != null && may.length > 0 )
563            {
564                buf.append( "MAY\n" );
565    
566                for ( AttributeType attributeType:may )
567                {
568                    buf.append( '\t' );
569                    buf.append( attributeType.getOid() );
570                    buf.append( '\n' );
571                }
572            }
573    
574            buf.append( " )" );
575            return buf.toString();
576        }
577    
578    
579        /**
580         * Generates the DITStructureRuleDescription for a DITStructureRule as
581         * defined by the syntax: 1.3.6.1.4.1.1466.115.121.1.17. Only the right hand
582         * side of the description starting at the openning parenthesis is
583         * generated: that is 'DITStructureRuleDescription = ' is not generated.
584         * 
585         * <pre>
586         *  DITStructureRuleDescription = &quot;(&quot; whsp
587         *      ruleidentifier whsp           ; DITStructureRule identifier
588         *      [ &quot;NAME&quot; qdescrs ]
589         *      [ &quot;DESC&quot; qdstring ]
590         *      [ &quot;OBSOLETE&quot; whsp ]
591         *      &quot;FORM&quot; woid whsp              ; NameForm
592         *      [ &quot;SUP&quot; ruleidentifiers whsp ]; superior DITStructureRules
593         *  &quot;)&quot;
594         * </pre>
595         * 
596         * @param dITStructureRule
597         *            the DITStructureRule to generate the description for
598         * @return the description in the DITStructureRuleDescription syntax
599         * @throws NamingException If an error is raised while accessing some of the dITStructureRule
600         * data 
601         */
602        public static String getDescription( DITStructureRule dITStructureRule ) throws NamingException
603        {
604            StringBuffer buf = new StringBuffer( "( " );
605            buf.append( dITStructureRule.getOid() );
606            buf.append( '\n' );
607    
608            buf.append( "NAME " );
609            buf.append( dITStructureRule.getName() );
610            buf.append( '\n' );
611    
612            if ( dITStructureRule.getDescription() != null )
613            {
614                buf.append( "DESC " );
615                buf.append( dITStructureRule.getDescription() );
616                buf.append( '\n' );
617            }
618    
619            if ( dITStructureRule.isObsolete() )
620            {
621                buf.append( "OBSOLETE" );
622                buf.append( '\n' );
623            }
624    
625            buf.append( "FORM " );
626            buf.append( dITStructureRule.getNameForm().getOid() );
627            buf.append( '\n' );
628    
629            DITStructureRule[] sups = dITStructureRule.getSuperClasses();
630            if ( sups != null && sups.length > 0 )
631            {
632                buf.append( "SUP\n" );
633                
634                for ( DITStructureRule sup:sups )
635                {
636                    buf.append( '\t' );
637                    buf.append( sup.getOid() );
638                    buf.append( '\n' );
639                }
640            }
641    
642            buf.append( " )" );
643            return buf.toString();
644        }
645    
646    
647        /**
648         * Generates the SyntaxDescription for a Syntax as defined by the syntax:
649         * 1.3.6.1.4.1.1466.115.121.1.54. Only the right hand side of the
650         * description starting at the openning parenthesis is generated: that is
651         * 'SyntaxDescription = ' is not generated.
652         * 
653         * <pre>
654         *  SyntaxDescription = &quot;(&quot; whsp
655         *      numericoid whsp
656         *      [ &quot;DESC&quot; qdstring ]
657         *  whsp &quot;)&quot;
658         * </pre>
659         * 
660         * @param syntax
661         *            the Syntax to generate a description for
662         * @return the description in the SyntaxDescription syntax
663         */
664        public static String getDescription( Syntax syntax )
665        {
666            StringBuffer buf = new StringBuffer( "( " );
667            buf.append( syntax.getOid() );
668            buf.append( '\n' );
669    
670            if ( syntax.getDescription() != null )
671            {
672                buf.append( "DESC " );
673                buf.append( syntax.getDescription() );
674                buf.append( '\n' );
675            }
676    
677            buf.append( " )" );
678            return buf.toString();
679        }
680    }