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.HashSet;
024    import java.util.List;
025    import java.util.Map;
026    import java.util.Set;
027    import java.util.UUID;
028    
029    import org.apache.directory.shared.i18n.I18n;
030    import org.apache.directory.shared.ldap.constants.MetaSchemaConstants;
031    import org.apache.directory.shared.ldap.entry.Entry;
032    import org.apache.directory.shared.ldap.entry.EntryAttribute;
033    import org.apache.directory.shared.ldap.entry.Modification;
034    import org.apache.directory.shared.ldap.entry.Value;
035    import org.apache.directory.shared.ldap.exception.LdapException;
036    import org.apache.directory.shared.ldap.util.StringTools;
037    
038    
039    /**
040     * Various utility methods for schema functions and objects.
041     * 
042     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
043     * @version $Rev: 928015 $
044     */
045    public class SchemaUtils
046    {
047        /**
048         * Gets the target entry as it would look after a modification operation 
049         * were performed on it.
050         * 
051         * @param mods the modifications performed on the entry
052         * @param entry the source entry that is modified
053         * @return the resultant entry after the modifications have taken place
054         * @throws LdapException if there are problems accessing attributes
055         */
056        public static Entry getTargetEntry( List<? extends Modification> mods, Entry entry )
057            throws LdapException
058        {
059            Entry targetEntry = entry.clone();
060    
061            for ( Modification mod : mods )
062            {
063                String id = mod.getAttribute().getId();
064    
065                switch ( mod.getOperation() )
066                {
067                    case REPLACE_ATTRIBUTE :
068                        targetEntry.put( mod.getAttribute() );
069                        break;
070    
071                    case ADD_ATTRIBUTE :
072                        EntryAttribute combined = mod.getAttribute().clone();
073                        EntryAttribute toBeAdded = mod.getAttribute();
074                        EntryAttribute existing = entry.get( id );
075    
076                        if ( existing != null )
077                        {
078                            for ( Value<?> value:existing )
079                            {
080                                combined.add( value );
081                            }
082                        }
083    
084                        for ( Value<?> value:toBeAdded )
085                        {
086                            combined.add( value );
087                        }
088    
089                        targetEntry.put( combined );
090                        break;
091    
092                    case REMOVE_ATTRIBUTE :
093                        EntryAttribute toBeRemoved = mod.getAttribute();
094    
095                        if ( toBeRemoved.size() == 0 )
096                        {
097                            targetEntry.removeAttributes( id );
098                        }
099                        else
100                        {
101                            existing = targetEntry.get( id );
102    
103                            if ( existing != null )
104                            {
105                                for ( Value<?> value:toBeRemoved )
106                                {
107                                    existing.remove( value );
108                                }
109                            }
110                        }
111    
112                        break;
113    
114                    default:
115                        throw new IllegalStateException( I18n.err( I18n.ERR_04328, mod.getOperation() ) );
116                }
117            }
118    
119            return targetEntry;
120        }
121    
122    
123        // ------------------------------------------------------------------------
124        // qdescrs rendering operations
125        // ------------------------------------------------------------------------
126    
127        /**
128         * Renders qdescrs into an existing buffer.
129         * 
130         * @param buf
131         *            the string buffer to render the quoted description strs into
132         * @param qdescrs
133         *            the quoted description strings to render
134         * @return the same string buffer that was given for call chaining
135         */
136        public static StringBuffer render( StringBuffer buf, List<String> qdescrs )
137        {
138            if ( ( qdescrs == null ) || ( qdescrs.size() == 0 ) )
139            {
140                return buf;
141            }
142            else if ( qdescrs.size() == 1 )
143            {
144                buf.append( "'" ).append( qdescrs.get( 0 ) ).append( "'" );
145            }
146            else
147            {
148                buf.append( "( " );
149                
150                for ( String qdescr : qdescrs )
151                {
152                    buf.append( "'" ).append( qdescr ).append( "' " );
153                }
154                
155                buf.append( ")" );
156            }
157    
158            return buf;
159        }
160    
161    
162        /**
163         * Renders qdescrs into a new buffer.<br>
164         * <pre>
165         * descrs ::= qdescr | '(' WSP qdescrlist WSP ')'
166         * qdescrlist ::= [ qdescr ( SP qdescr )* ]
167         * qdescr     ::= SQUOTE descr SQUOTE
168         * </pre>
169         * @param qdescrs the quoted description strings to render
170         * @return the string buffer the qdescrs are rendered into
171         */
172        /* No qualifier */ static StringBuffer renderQDescrs( StringBuffer buf, List<String> qdescrs )
173        {
174            if ( ( qdescrs == null ) || ( qdescrs.size() == 0 ) )
175            {
176                return buf;
177            }
178            
179            if ( qdescrs.size() == 1 )
180            {
181                buf.append( '\'' ).append( qdescrs.get( 0 ) ).append( '\'' );
182            }
183            else
184            {
185                buf.append( "( " );
186                
187                for ( String qdescr : qdescrs )
188                {
189                    buf.append( '\'' ).append( qdescr ).append( "' " );
190                }
191                
192                buf.append( ")" );
193            }
194    
195            return buf;
196        }
197    
198    
199        /**
200         * Renders oids into a new buffer.<br>
201         * <pre>
202         * oids    ::= oid | '(' WSP oidlist WSP ')'
203         * oidlist ::= oid ( WSP '$' WSP oid )*
204         * </pre>
205         * 
206         * @param qdescrs the quoted description strings to render
207         * @return the string buffer the qdescrs are rendered into
208         */
209        private static StringBuffer renderOids( StringBuffer buf, List<String> oids )
210        {
211            if ( oids.size() == 1 )
212            {
213                buf.append( oids.get( 0 ) );
214            }
215            else
216            {
217                buf.append( "( " );
218                
219                boolean isFirst = true;
220                
221                for ( String oid : oids )
222                {
223                    if ( isFirst )
224                    {
225                        isFirst = false;
226                    }
227                    else
228                    {
229                        buf.append( " $ " );
230                    }
231                    
232                    buf.append( oid );
233                }
234                
235                buf.append( " )" );
236            }
237    
238            return buf;
239        }
240    
241    
242        /**
243         * Renders QDString into a new buffer.<br>
244         * <pre>
245         * 
246         * @param qdescrs the quoted description strings to render
247         * @return the string buffer the qdescrs are rendered into
248         */
249        private static StringBuffer renderQDString( StringBuffer buf, String qdString )
250        {
251            buf.append(  '\'' );
252            
253            for ( char c : qdString.toCharArray() )
254            {
255                switch ( c )
256                {
257                    case 0x27 :
258                        buf.append( "\\27" );
259                        break;
260                        
261                    case 0x5C :
262                        buf.append( "\\5C" );
263                        break;
264                        
265                    default :
266                        buf.append( c );
267                        break;
268                }
269            }
270            
271            buf.append(  '\'' );
272         
273            return buf;
274        }
275    
276        // ------------------------------------------------------------------------
277        // objectClass list rendering operations
278        // ------------------------------------------------------------------------
279    
280        /**
281         * Renders a list of object classes for things like a list of superior
282         * objectClasses using the ( oid $ oid ) format.
283         * 
284         * @param ocs
285         *            the objectClasses to list
286         * @return a buffer which contains the rendered list
287         */
288        public static StringBuffer render( ObjectClass[] ocs )
289        {
290            StringBuffer buf = new StringBuffer();
291            
292            return render( buf, ocs );
293        }
294    
295    
296        /**
297         * Renders a list of object classes for things like a list of superior
298         * objectClasses using the ( oid $ oid ) format into an existing buffer.
299         * 
300         * @param buf
301         *            the string buffer to render the list of objectClasses into
302         * @param ocs
303         *            the objectClasses to list
304         * @return a buffer which contains the rendered list
305         */
306        public static StringBuffer render( StringBuffer buf, ObjectClass[] ocs )
307        {
308            if ( ocs == null || ocs.length == 0 )
309            {
310                return buf;
311            }
312            else if ( ocs.length == 1 )
313            {
314                buf.append( ocs[0].getName() );
315            }
316            else
317            {
318                buf.append( "( " );
319                
320                for ( int ii = 0; ii < ocs.length; ii++ )
321                {
322                    if ( ii + 1 < ocs.length )
323                    {
324                        buf.append( ocs[ii].getName() ).append( " $ " );
325                    }
326                    else
327                    {
328                        buf.append( ocs[ii].getName() );
329                    }
330                }
331                
332                buf.append( " )" );
333            }
334    
335            return buf;
336        }
337    
338    
339        // ------------------------------------------------------------------------
340        // attributeType list rendering operations
341        // ------------------------------------------------------------------------
342    
343        /**
344         * Renders a list of attributeTypes for things like the must or may list of
345         * objectClasses using the ( oid $ oid ) format.
346         * 
347         * @param ats
348         *            the attributeTypes to list
349         * @return a buffer which contains the rendered list
350         */
351        public static StringBuffer render( AttributeType[] ats )
352        {
353            StringBuffer buf = new StringBuffer();
354            return render( buf, ats );
355        }
356    
357    
358        /**
359         * Renders a list of attributeTypes for things like the must or may list of
360         * objectClasses using the ( oid $ oid ) format into an existing buffer.
361         * 
362         * @param buf
363         *            the string buffer to render the list of attributeTypes into
364         * @param ats
365         *            the attributeTypes to list
366         * @return a buffer which contains the rendered list
367         */
368        public static StringBuffer render( StringBuffer buf, AttributeType[] ats )
369        {
370            if ( ats == null || ats.length == 0 )
371            {
372                return buf;
373            }
374            else if ( ats.length == 1 )
375            {
376                buf.append( ats[0].getName() );
377            }
378            else
379            {
380                buf.append( "( " );
381                for ( int ii = 0; ii < ats.length; ii++ )
382                {
383                    if ( ii + 1 < ats.length )
384                    {
385                        buf.append( ats[ii].getName() ).append( " $ " );
386                    }
387                    else
388                    {
389                        buf.append( ats[ii].getName() );
390                    }
391                }
392                buf.append( " )" );
393            }
394    
395            return buf;
396        }
397    
398    
399        // ------------------------------------------------------------------------
400        // schema object rendering operations
401        // ------------------------------------------------------------------------
402    
403        /**
404         * Renders an objectClass into a new StringBuffer according to the Object
405         * Class Description Syntax 1.3.6.1.4.1.1466.115.121.1.37. The syntax is
406         * described in detail within section 4.1.1. of LDAPBIS [<a
407         * href="http://ietf.org/internet-drafts/draft-ietf-ldapbis-models-12.txt">MODELS</a>]
408         * which is replicated here for convenience:
409         * 
410         * <pre>
411         *  4.1.1. Object Class Definitions
412         * 
413         *   Object Class definitions are written according to the ABNF:
414         * 
415         *     ObjectClassDescription = LPAREN WSP
416         *         numericoid                 ; object identifier
417         *         [ SP &quot;NAME&quot; SP qdescrs ]   ; short names (descriptors)
418         *         [ SP &quot;DESC&quot; SP qdstring ]  ; description
419         *         [ SP &quot;OBSOLETE&quot; ]          ; not active
420         *         [ SP &quot;SUP&quot; SP oids ]       ; superior object classes
421         *         [ SP kind ]                ; kind of class
422         *         [ SP &quot;MUST&quot; SP oids ]      ; attribute types
423         *         [ SP &quot;MAY&quot; SP oids ]       ; attribute types
424         *         extensions WSP RPAREN
425         * 
426         *     kind = &quot;ABSTRACT&quot; / &quot;STRUCTURAL&quot; / &quot;AUXILIARY&quot;
427         * 
428         *   where:
429         *     &lt;numericoid&gt; is object identifier assigned to this object class;
430         *     NAME &lt;qdescrs&gt; are short names (descriptors) identifying this object
431         *         class;
432         *     DESC &lt;qdstring&gt; is a short descriptive string;
433         *     OBSOLETE indicates this object class is not active;
434         *     SUP &lt;oids&gt; specifies the direct superclasses of this object class;
435         *     the kind of object class is indicated by one of ABSTRACT,
436         *         STRUCTURAL, or AUXILIARY, default is STRUCTURAL;
437         *     MUST and MAY specify the sets of required and allowed attribute
438         *         types, respectively; and
439         *     &lt;extensions&gt; describe extensions.
440         * </pre>
441         * @param oc the objectClass to render the description of
442         * @return the buffer containing the objectClass description
443         * @throws LdapException if there are any problems accessing objectClass
444         * information
445         */
446        public static StringBuffer render( ObjectClass oc ) throws LdapException
447        {
448            StringBuffer buf = new StringBuffer();
449            buf.append( "( " ).append( oc.getOid() );
450            
451            List<String> names = oc.getNames();
452    
453            if ( ( names != null ) && ( names.size() > 0 ) )
454            {
455                buf.append( " NAME " );
456                renderQDescrs( buf, names );
457            }
458    
459            if ( oc.getDescription() != null )
460            {
461                buf.append( " DESC " );
462                renderQDString( buf, oc.getDescription() );
463            }
464    
465            if ( oc.isObsolete() )
466            {
467                buf.append( " OBSOLETE" );
468            }
469    
470            List<String> superiorOids = oc.getSuperiorOids();
471            
472            if ( ( superiorOids != null ) && ( superiorOids.size() > 0 ) )
473            {
474                buf.append( " SUP " );
475                renderOids( buf, superiorOids );
476            }
477    
478            if ( oc.getType() != null )
479            {
480                buf.append( " " ).append( oc.getType() );
481            }
482    
483            List<String> must = oc.getMustAttributeTypeOids();
484            
485            if ( ( must != null ) && ( must.size() > 0 ) )
486            {
487                buf.append( " MUST " );
488                renderOids( buf, must );
489            }
490    
491            List<String> may = oc.getMayAttributeTypeOids();
492    
493            if ( ( may != null ) && ( may.size() > 0 ) )
494            {
495                buf.append( " MAY " );
496                renderOids( buf, may );
497            }
498    
499            buf.append( " X-SCHEMA '" );
500            buf.append( oc.getSchemaName() );
501            buf.append( "'" );
502    
503            // @todo extensions are not presently supported and skipped
504            // the extensions would go here before closing off the description
505    
506            buf.append( " )" );
507    
508            return buf;
509        }
510    
511    
512        /**
513         * Renders an attributeType into a new StringBuffer according to the
514         * Attribute Type Description Syntax 1.3.6.1.4.1.1466.115.121.1.3. The
515         * syntax is described in detail within section 4.1.2. of LDAPBIS [<a
516         * href="http://ietf.org/internet-drafts/draft-ietf-ldapbis-models-12.txt">MODELS</a>]
517         * which is replicated here for convenience:
518         * 
519         * <pre>
520         *  4.1.2. Attribute Types
521         * 
522         *   Attribute Type definitions are written according to the ABNF:
523         * 
524         *   AttributeTypeDescription = LPAREN WSP
525         *         numericoid                    ; object identifier
526         *         [ SP &quot;NAME&quot; SP qdescrs ]      ; short names (descriptors)
527         *         [ SP &quot;DESC&quot; SP qdstring ]     ; description
528         *         [ SP &quot;OBSOLETE&quot; ]             ; not active
529         *         [ SP &quot;SUP&quot; SP oid ]           ; supertype
530         *         [ SP &quot;EQUALITY&quot; SP oid ]      ; equality matching rule
531         *         [ SP &quot;ORDERING&quot; SP oid ]      ; ordering matching rule
532         *         [ SP &quot;SUBSTR&quot; SP oid ]        ; substrings matching rule
533         *         [ SP &quot;SYNTAX&quot; SP noidlen ]    ; value syntax
534         *         [ SP &quot;SINGLE-VALUE&quot; ]         ; single-value
535         *         [ SP &quot;COLLECTIVE&quot; ]           ; collective
536         *         [ SP &quot;NO-USER-MODIFICATION&quot; ] ; not user modifiable
537         *         [ SP &quot;USAGE&quot; SP usage ]       ; usage
538         *         extensions WSP RPAREN         ; extensions
539         * 
540         *     usage = &quot;userApplications&quot;     /  ; user
541         *             &quot;directoryOperation&quot;   /  ; directory operational
542         *             &quot;distributedOperation&quot; /  ; DSA-shared operational
543         *             &quot;dSAOperation&quot;            ; DSA-specific operational
544         * 
545         *   where:
546         *     &lt;numericoid&gt; is object identifier assigned to this attribute type;
547         *     NAME &lt;qdescrs&gt; are short names (descriptors) identifying this
548         *         attribute type;
549         *     DESC &lt;qdstring&gt; is a short descriptive string;
550         *     OBSOLETE indicates this attribute type is not active;
551         *     SUP oid specifies the direct supertype of this type;
552         *     EQUALITY, ORDERING, SUBSTR provide the oid of the equality,
553         *         ordering, and substrings matching rules, respectively;
554         *     SYNTAX identifies value syntax by object identifier and may suggest
555         *         a minimum upper bound;
556         *     SINGLE-VALUE indicates attributes of this type are restricted to a
557         *         single value;
558         *     COLLECTIVE indicates this attribute type is collective
559         *         [X.501][RFC3671];
560         *     NO-USER-MODIFICATION indicates this attribute type is not user
561         *         modifiable;
562         *     USAGE indicates the application of this attribute type; and
563         *     &lt;extensions&gt; describe extensions.
564         * </pre>
565         * @param at the AttributeType to render the description for
566         * @return the StringBuffer containing the rendered attributeType description
567         * @throws LdapException if there are problems accessing the objects
568         * associated with the attribute type.
569         */
570        public static StringBuffer render( AttributeType at ) throws LdapException
571        {
572            StringBuffer buf = new StringBuffer();
573            buf.append( "( " ).append( at.getOid() );
574            List<String> names = at.getNames();
575    
576            if ( ( names != null ) && ( names.size() > 0 ) )
577            {
578                buf.append( " NAME " );
579                renderQDescrs( buf, names );
580            }
581    
582            if ( at.getDescription() != null )
583            {
584                buf.append( " DESC " );
585                renderQDString( buf, at.getDescription() );
586            }
587    
588            if ( at.isObsolete() )
589            {
590                buf.append( " OBSOLETE" );
591            }
592    
593            if ( at.getSuperior() != null )
594            {
595                buf.append( " SUP " ).append( at.getSuperior().getName() );
596            }
597    
598            if ( at.getEquality() != null )
599            {
600                buf.append( " EQUALITY " ).append( at.getEquality().getName() );
601            }
602    
603            if ( at.getOrdering() != null )
604            {
605                buf.append( " ORDERING " ).append( at.getOrdering().getName() );
606            }
607    
608            if ( at.getSubstring() != null )
609            {
610                buf.append( " SUBSTR " ).append( at.getSubstring().getName() );
611            }
612    
613            if ( at.getSyntax() != null )
614            {
615                buf.append( " SYNTAX " ).append( at.getSyntax().getOid() );
616    
617                if ( at.getSyntaxLength() > 0 )
618                {
619                    buf.append( "{" ).append( at.getSyntaxLength() ).append( "}" );
620                }
621            }
622    
623            if ( at.isSingleValued() )
624            {
625                buf.append( " SINGLE-VALUE" );
626            }
627    
628            if ( at.isCollective() )
629            {
630                buf.append( " COLLECTIVE" );
631            }
632    
633            if ( !at.isUserModifiable() )
634            {
635                buf.append( " NO-USER-MODIFICATION" );
636            }
637    
638            if ( at.getUsage() != null )
639            {
640                buf.append( " USAGE " ).append( UsageEnum.render( at.getUsage() ) );
641            }
642    
643            buf.append( " X-SCHEMA '" );
644            buf.append( at.getSchemaName() );
645            buf.append( "'" );
646    
647            // @todo extensions are not presently supported and skipped
648            // the extensions would go here before closing off the description
649    
650            buf.append( " )" );
651    
652            return buf;
653        }
654    
655    
656        /**
657         * Renders an attributeType description object into a new StringBuffer
658         * according to the Attribute Type Description Syntax defined in MODELS
659         * 1.3.6.1.4.1.1466.115.121.1.3. The syntax is described in detail within
660         * section 4.1.2. of (@TODO NEEDS TO CHANGE SINCE THIS IS NOW AN RFC) LDAPBIS [<a
661         * href="http://ietf.org/internet-drafts/draft-ietf-ldapbis-models-12.txt">MODELS</a>]
662         * which is replicated here for convenience:
663         *
664         * <pre>
665         *  4.1.2. Attribute Types
666         *
667         *   Attribute Type definitions are written according to the ABNF:
668         *
669         *   AttributeTypeDescription = LPAREN WSP
670         *         numericoid                    ; object identifier
671         *         [ SP &quot;NAME&quot; SP qdescrs ]      ; short names (descriptors)
672         *         [ SP &quot;DESC&quot; SP qdstring ]     ; description
673         *         [ SP &quot;OBSOLETE&quot; ]             ; not active
674         *         [ SP &quot;SUP&quot; SP oid ]           ; supertype
675         *         [ SP &quot;EQUALITY&quot; SP oid ]      ; equality matching rule
676         *         [ SP &quot;ORDERING&quot; SP oid ]      ; ordering matching rule
677         *         [ SP &quot;SUBSTR&quot; SP oid ]        ; substrings matching rule
678         *         [ SP &quot;SYNTAX&quot; SP noidlen ]    ; value syntax
679         *         [ SP &quot;SINGLE-VALUE&quot; ]         ; single-value
680         *         [ SP &quot;COLLECTIVE&quot; ]           ; collective
681         *         [ SP &quot;NO-USER-MODIFICATION&quot; ] ; not user modifiable
682         *         [ SP &quot;USAGE&quot; SP usage ]       ; usage
683         *         extensions WSP RPAREN         ; extensions
684         *
685         *     usage = &quot;userApplications&quot;     /  ; user
686         *             &quot;directoryOperation&quot;   /  ; directory operational
687         *             &quot;distributedOperation&quot; /  ; DSA-shared operational
688         *             &quot;dSAOperation&quot;            ; DSA-specific operational
689         *
690         *   where:
691         *     &lt;numericoid&gt; is object identifier assigned to this attribute type;
692         *     NAME &lt;qdescrs&gt; are short names (descriptors) identifying this
693         *         attribute type;
694         *     DESC &lt;qdstring&gt; is a short descriptive string;
695         *     OBSOLETE indicates this attribute type is not active;
696         *     SUP oid specifies the direct supertype of this type;
697         *     EQUALITY, ORDERING, SUBSTR provide the oid of the equality,
698         *         ordering, and substrings matching rules, respectively;
699         *     SYNTAX identifies value syntax by object identifier and may suggest
700         *         a minimum upper bound;
701         *     SINGLE-VALUE indicates attributes of this type are restricted to a
702         *         single value;
703         *     COLLECTIVE indicates this attribute type is collective
704         *         [X.501][RFC3671];
705         *     NO-USER-MODIFICATION indicates this attribute type is not user
706         *         modifiable;
707         *     USAGE indicates the application of this attribute type; and
708         *     &lt;extensions&gt; describe extensions.
709         * </pre>
710         * @param atd the AttributeTypeDescription to render the description for
711         * @return the StringBuffer containing the rendered attributeType description
712         * @throws LdapException if there are problems accessing the objects
713         * associated with the attribute type.
714         *
715        public static StringBuffer render( AttributeType attributeType )
716        {
717            StringBuffer buf = new StringBuffer();
718            buf.append( "( " ).append( attributeType.getOid() );
719    
720            if ( attributeType.getNames() != null && attributeType.getNames().size() > 0 )
721            {
722                buf.append( " NAME " );
723                render( buf, attributeType.getNames() ).append( " " );
724            }
725            else
726            {
727                buf.append( " " );
728            }
729    
730            if ( attributeType.getDescription() != null )
731            {
732                buf.append( "DESC " ).append( "'" ).append( attributeType.getDescription() ).append( "' " );
733            }
734    
735            if ( attributeType.isObsolete() )
736            {
737                buf.append( " OBSOLETE" );
738            }
739    
740            if ( attributeType.getSupOid() != null )
741            {
742                buf.append( " SUP " ).append( attributeType.getSupOid() );
743            }
744    
745            if ( attributeType.getEqualityOid() != null )
746            {
747                buf.append( " EQUALITY " ).append( attributeType.getEqualityOid() );
748            }
749    
750            if ( attributeType.getOrderingOid() != null )
751            {
752                buf.append( " ORDERING " ).append( attributeType.getOrderingOid() );
753            }
754    
755            if ( attributeType.getSubstrOid() != null )
756            {
757                buf.append( " SUBSTR " ).append( attributeType.getSubstrOid() );
758            }
759    
760            if ( attributeType.getSyntax() != null )
761            {
762                buf.append( " SYNTAX " ).append( attributeType.getSyntax() );
763    
764                if ( attributeType.getLength() > 0 )
765                {
766                    buf.append( "{" ).append( attributeType.getLength() ).append( "}" );
767                }
768            }
769    
770            if ( attributeType.isSingleValue() )
771            {
772                buf.append( " SINGLE-VALUE" );
773            }
774    
775            if ( attributeType.isCollective() )
776            {
777                buf.append( " COLLECTIVE" );
778            }
779    
780            if ( !attributeType.isCanUserModify() )
781            {
782                buf.append( " NO-USER-MODIFICATION" );
783            }
784    
785            if ( attributeType.getUsage() != null )
786            {
787                buf.append( " USAGE " ).append( UsageEnum.render( attributeType.getUsage() ) );
788            }
789    
790            return buf.append( render( attributeType.getExtensions() ) ).append( ")" );
791        }
792    
793    
794        /**
795         * Renders the schema extensions into a new StringBuffer.
796         *
797         * @param extensions the schema extensions map with key and values
798         * @return a StringBuffer with the extensions component of a syntax description
799         */
800        public static StringBuffer render( Map<String, List<String>> extensions )
801        {
802            StringBuffer buf = new StringBuffer();
803    
804            if ( extensions.isEmpty() )
805            {
806                return buf;
807            }
808    
809            for ( String key : extensions.keySet() )
810            {
811                buf.append( " " ).append( key ).append( " " );
812    
813                List<String> values = extensions.get( key );
814    
815                // For extensions without values like X-IS-HUMAN-READIBLE
816                if ( values == null || values.isEmpty() )
817                {
818                    continue;
819                }
820    
821                // For extensions with a single value we can use one qdstring like 'value'
822                if ( values.size() == 1 )
823                {
824                    buf.append( "'" ).append( values.get( 0 ) ).append( "' " );
825                    continue;
826                }
827    
828                // For extensions with several values we have to surround whitespace
829                // separated list of qdstrings like ( 'value0' 'value1' 'value2' )
830                buf.append( "( " );
831                for ( String value : values )
832                {
833                    buf.append( "'" ).append( value ).append( "' " );
834                }
835                buf.append( ")" );
836            }
837    
838            if ( buf.charAt( buf.length() - 1 ) != ' ' )
839            {
840                buf.append( " " );
841            }
842    
843            return buf;
844        }
845    
846    
847        /**
848         * Renders an matchingRule into a new StringBuffer according to the
849         * MatchingRule Description Syntax 1.3.6.1.4.1.1466.115.121.1.30. The syntax
850         * is described in detail within section 4.1.3. of LDAPBIS [<a
851         * href="http://ietf.org/internet-drafts/draft-ietf-ldapbis-models-12.txt">MODELS</a>]
852         * which is replicated here for convenience:
853         * 
854         * <pre>
855         *  4.1.3. Matching Rules
856         * 
857         *   Matching rules are used in performance of attribute value assertions,
858         *   such as in performance of a Compare operation.  They are also used in
859         *   evaluation of a Search filters, in determining which individual values
860         *   are be added or deleted during performance of a Modify operation, and
861         *   used in comparison of distinguished names.
862         * 
863         *   Each matching rule is identified by an object identifier (OID) and,
864         *   optionally, one or more short names (descriptors).
865         * 
866         *   Matching rule definitions are written according to the ABNF:
867         * 
868         *   MatchingRuleDescription = LPAREN WSP
869         *        numericoid                 ; object identifier
870         *         [ SP &quot;NAME&quot; SP qdescrs ]   ; short names (descriptors)
871         *         [ SP &quot;DESC&quot; SP qdstring ]  ; description
872         *         [ SP &quot;OBSOLETE&quot; ]          ; not active
873         *         SP &quot;SYNTAX&quot; SP numericoid  ; assertion syntax
874         *         extensions WSP RPAREN      ; extensions
875         * 
876         *   where:
877         *     &lt;numericoid&gt; is object identifier assigned to this matching rule;
878         *     NAME &lt;qdescrs&gt; are short names (descriptors) identifying this
879         *         matching rule;
880         *     DESC &lt;qdstring&gt; is a short descriptive string;
881         *     OBSOLETE indicates this matching rule is not active;
882         *     SYNTAX identifies the assertion syntax (the syntax of the assertion
883         *         value) by object identifier; and
884         *     &lt;extensions&gt; describe extensions.
885         * </pre>
886         * @param mr the MatchingRule to render the description for
887         * @return the StringBuffer containing the rendered matchingRule description
888         * @throws LdapException if there are problems accessing the objects
889         * associated with the MatchingRule.
890         */
891        public static StringBuffer render( MatchingRule mr ) throws LdapException
892        {
893            StringBuffer buf = new StringBuffer();
894            buf.append( "( " ).append( mr.getOid() );
895            
896            List<String> names = mr.getNames();
897    
898            if ( ( names != null ) && ( names.size() > 0 ) )
899            {
900                buf.append( " NAME " );
901                renderQDescrs( buf, names );
902            }
903    
904            if ( mr.getDescription() != null )
905            {
906                buf.append( " DESC " );
907                renderQDString( buf, mr.getDescription() );
908            }
909    
910            if ( mr.isObsolete() )
911            {
912                buf.append( " OBSOLETE" );
913            }
914    
915            buf.append( " SYNTAX " ).append( mr.getSyntax().getOid() );
916    
917            buf.append( " X-SCHEMA '" );
918            buf.append( mr.getSchemaName() );
919            buf.append( "'" );
920    
921            // @todo extensions are not presently supported and skipped
922            // the extensions would go here before closing off the description
923    
924            buf.append( " )" );
925    
926            return buf;
927        }
928    
929    
930        /**
931         * Renders a Syntax into a new StringBuffer according to the LDAP Syntax
932         * Description Syntax 1.3.6.1.4.1.1466.115.121.1.54. The syntax is described
933         * in detail within section 4.1.5. of LDAPBIS [<a
934         * href="http://ietf.org/internet-drafts/draft-ietf-ldapbis-models-12.txt">MODELS</a>]
935         * which is replicated here for convenience:
936         * 
937         * <pre>
938         *  LDAP syntax definitions are written according to the ABNF:
939         * 
940         *   SyntaxDescription = LPAREN WSP
941         *       numericoid                 ; object identifier
942         *       [ SP &quot;DESC&quot; SP qdstring ]  ; description
943         *       extensions WSP RPAREN      ; extensions
944         * 
945         *  where:
946         *   &lt;numericoid&gt; is the object identifier assigned to this LDAP syntax;
947         *   DESC &lt;qdstring&gt; is a short descriptive string; and
948         *   &lt;extensions&gt; describe extensions.
949         * </pre>
950         * @param syntax the Syntax to render the description for
951         * @return the StringBuffer containing the rendered syntax description
952         */
953        public static StringBuffer render( LdapSyntax syntax )
954        {
955            StringBuffer buf = new StringBuffer();
956            buf.append( "( " ).append( syntax.getOid() );
957    
958            if ( syntax.getDescription() != null )
959            {
960                buf.append( " DESC " );
961                renderQDString( buf, syntax.getDescription() );
962            }
963    
964            buf.append( " X-SCHEMA '" );
965            buf.append( syntax.getSchemaName() );
966    
967            if ( syntax.isHumanReadable() )
968            {
969                buf.append( "' X-IS-HUMAN-READABLE 'true'" );
970            }
971            else
972            {
973                buf.append( "' X-IS-HUMAN-READABLE 'false'" );
974            }
975    
976            // @todo extensions are not presently supported and skipped
977            // the extensions would go here before closing off the description
978    
979            buf.append( " )" );
980    
981            return buf;
982        }
983    
984    
985        /**
986         * NOT FULLY IMPLEMENTED!
987         */
988        public static StringBuffer render( MatchingRuleUse mru )
989        {
990            StringBuffer buf = new StringBuffer();
991            buf.append( "( " ).append( mru.getOid() );
992            
993            List<String> names = mru.getNames();
994    
995            if ( ( names != null ) && ( names.size() > 0 ) )
996            {
997                buf.append( " NAME " );
998                renderQDescrs( buf, names );
999            }
1000    
1001            if ( mru.getDescription() != null )
1002            {
1003                buf.append( " DESC " );
1004                renderQDString( buf, mru.getDescription() );
1005            }
1006            
1007            if ( mru.isObsolete )
1008            {
1009                buf.append(  " OBSOLETE" );
1010            }
1011            
1012            List<String> applies = mru.getApplicableAttributeOids();
1013            
1014            if ( ( applies != null ) && ( applies.size() > 0 ) )
1015            {
1016                buf.append( " APPLIES " );
1017                renderOids( buf, applies );
1018            }
1019            
1020            buf.append( " X-SCHEMA '" );
1021            buf.append( mru.getSchemaName() );
1022            buf.append( "'" );
1023    
1024            // @todo extensions are not presently supported and skipped
1025            // the extensions would go here before closing off the description
1026    
1027            buf.append( " )" );
1028    
1029            return buf;
1030        }
1031    
1032    
1033        /**
1034         * NOT FULLY IMPLEMENTED!
1035         */
1036        public static StringBuffer render( DITContentRule dcr )
1037        {
1038            StringBuffer buf = new StringBuffer();
1039            buf.append( "( " ).append( dcr.getOid() );
1040            
1041            List<String> names = dcr.getNames();
1042            
1043            if ( ( names != null ) && ( names.size() > 0 ) )
1044            {
1045                buf.append( " NAME " );
1046                renderQDescrs( buf, names );
1047            }
1048            
1049            if ( dcr.getDescription() != null )
1050            {
1051                buf.append( " DESC " );
1052                renderQDString( buf, dcr.getDescription() );
1053            }
1054    
1055            if ( dcr.isObsolete )
1056            {
1057                buf.append( " OBSOLETE" );
1058            }
1059    
1060            List<String> aux = dcr.getAuxObjectClassOids();
1061            
1062            if ( ( aux != null ) && ( aux.size() > 0 ) )
1063            {
1064                buf.append( " AUX " );
1065                renderOids( buf, aux );
1066            }
1067    
1068            List<String> must = dcr.getMustAttributeTypeOids();
1069            
1070            if ( ( must != null ) && ( must.size() > 0 ) )
1071            {
1072                buf.append( " MUST " );
1073                renderOids( buf, must );
1074            }
1075    
1076            List<String> may = dcr.getMayAttributeTypeOids();
1077            
1078            if ( ( may != null ) && ( may.size() > 0 ) )
1079            {
1080                buf.append( " MAY " );
1081                renderOids( buf, may );
1082            }
1083    
1084            List<String> not = dcr.getNotAttributeTypeOids();
1085            
1086            if ( ( not != null ) && ( not.size() > 0 ) )
1087            {
1088                buf.append( " AUX " );
1089                renderOids( buf, not );
1090            }
1091    
1092            buf.append( " X-SCHEMA '" );
1093            buf.append( dcr.getSchemaName() );
1094            buf.append( "'" );
1095    
1096            // @todo extensions are not presently supported and skipped
1097            // the extensions would go here before closing off the description
1098    
1099            buf.append( " )" );
1100    
1101            return buf;
1102        }
1103    
1104    
1105        /**
1106         * NOT FULLY IMPLEMENTED!
1107         */
1108        public static StringBuffer render( DITStructureRule dsr )
1109        {
1110            StringBuffer buf = new StringBuffer();
1111            buf.append( "( " ).append( dsr.getOid() );
1112            
1113            List<String> names = dsr.getNames();
1114            
1115            if ( ( names != null ) && ( names.size() > 0 ) )
1116            {
1117                buf.append( " NAME " );
1118                renderQDescrs( buf, names );
1119            }
1120            
1121            if ( dsr.getDescription() != null )
1122            {
1123                buf.append( " DESC " );
1124                renderQDString( buf, dsr.getDescription() );
1125            }
1126    
1127            if ( dsr.isObsolete )
1128            {
1129                buf.append( " OBSOLETE" );
1130            }
1131    
1132            buf.append( " FORM " ).append( dsr.getForm() );
1133    
1134            List<Integer> ruleIds = dsr.getSuperRules();
1135            // TODO : Add the rendering
1136    
1137            buf.append( " X-SCHEMA '" );
1138            buf.append( dsr.getSchemaName() );
1139            buf.append( "' )" );
1140    
1141            return buf;
1142        }
1143    
1144    
1145        /**
1146         * NOT FULLY IMPLEMENTED!
1147         */
1148        public static StringBuffer render( NameForm nf )
1149        {
1150            StringBuffer buf = new StringBuffer();
1151            buf.append( "( " ).append( nf.getOid() );
1152            
1153            List<String> names = nf.getNames();
1154    
1155            if ( ( names != null ) && ( names.size() > 0 ) )
1156            {
1157                buf.append( " NAME " );
1158                renderQDescrs( buf, names );
1159            }
1160    
1161            if ( nf.getDescription() != null )
1162            {
1163                buf.append( " DESC " );
1164                renderQDString( buf, nf.getDescription() );
1165            }
1166    
1167            if ( nf.isObsolete )
1168            {
1169                buf.append( " OBSOLETE" );
1170            }
1171    
1172            buf.append( " OC " );
1173            buf.append( nf.getStructuralObjectClass().getName() );
1174            
1175            buf.append( " MUST " );
1176            renderOids( buf, nf.getMustAttributeTypeOids() );
1177            
1178            List<String> may = nf.getMayAttributeTypeOids();
1179            
1180            if ( ( may != null ) && ( may.size() > 0 ) )
1181            {
1182                buf.append( " MAY " );
1183                renderOids( buf, may );
1184            }
1185    
1186            buf.append( " X-SCHEMA '" );
1187            buf.append( nf.getSchemaName() );
1188            buf.append( "' )" );
1189    
1190            return buf;
1191        }
1192    
1193    
1194        /**
1195         * Returns a String description of a schema. The resulting String format is :
1196         * <br>
1197         * (OID [DESC '<description>'] FQCN <fcqn> [BYTECODE <bytecode>] X-SCHEMA '<schema>')
1198         * <br>
1199         * @param description The description to transform to a String
1200         * @return
1201         */
1202        public static String render( LoadableSchemaObject description )
1203        {
1204            StringBuffer buf = new StringBuffer();
1205            buf.append( "( " ).append( description.getOid() );
1206    
1207            if ( description.getDescription() != null )
1208            {
1209                buf.append( " DESC " );
1210                renderQDString( buf, description.getDescription() );
1211            }
1212    
1213            buf.append( " FQCN " ).append( description.getFqcn() );
1214    
1215            if ( !StringTools.isEmpty( description.getBytecode() ) )
1216            {
1217                buf.append( " BYTECODE " ).append( description.getBytecode() );
1218            }
1219    
1220            buf.append( " X-SCHEMA '" );
1221            buf.append( getSchemaName( description ) );
1222            buf.append( "' )" );
1223    
1224            return buf.toString();
1225        }
1226    
1227    
1228        private static String getSchemaName( SchemaObject desc )
1229        {
1230            List<String> values = desc.getExtensions().get( MetaSchemaConstants.X_SCHEMA );
1231    
1232            if ( values == null || values.size() == 0 )
1233            {
1234                return MetaSchemaConstants.SCHEMA_OTHER;
1235            }
1236    
1237            return values.get( 0 );
1238        }
1239    
1240    
1241        /**
1242         * Remove the options from the attributeType, and returns the ID.
1243         * 
1244         * RFC 4512 :
1245         * attributedescription = attributetype options
1246         * attributetype = oid
1247         * options = *( SEMI option )
1248         * option = 1*keychar
1249         */
1250        public static String stripOptions( String attributeId )
1251        {
1252            int optionsPos = attributeId.indexOf( ";" ); 
1253            
1254            if ( optionsPos != -1 )
1255            {
1256                return attributeId.substring( 0, optionsPos );
1257            }
1258            else
1259            {
1260                return attributeId;
1261            }
1262        }
1263        
1264        /**
1265         * Get the options from the attributeType.
1266         * 
1267         * For instance, given :
1268         * jpegphoto;binary;lang=jp
1269         * 
1270         * your get back a set containing { "binary", "lang=jp" }
1271         */
1272        public static Set<String> getOptions( String attributeId )
1273        {
1274            int optionsPos = attributeId.indexOf( ";" ); 
1275    
1276            if ( optionsPos != -1 )
1277            {
1278                Set<String> options = new HashSet<String>();
1279                
1280                String[] res = attributeId.substring( optionsPos + 1 ).split( ";" );
1281                
1282                for ( String option:res )
1283                {
1284                    if ( !StringTools.isEmpty( option ) )
1285                    {
1286                        options.add( option );
1287                    }
1288                }
1289                
1290                return options;
1291            }
1292            else
1293            {
1294                return null;
1295            }
1296        }
1297        
1298        
1299        /**
1300         * Transform an UUID in a byte array
1301         * @param uuid The UUID to transform
1302         * @return The byte[] representing the UUID
1303         */
1304        public static byte[] uuidToBytes( UUID uuid )
1305        {
1306            Long low = uuid.getLeastSignificantBits();
1307            Long high = uuid.getMostSignificantBits();
1308            byte[] bytes=new byte[16];
1309            
1310            bytes[0]  = (byte) ((high & 0xff00000000000000L)>>56);
1311            bytes[1]  = (byte) ((high & 0x00ff000000000000L)>>48);
1312            bytes[2]  = (byte) ((high & 0x0000ff0000000000L)>>40);
1313            bytes[3]  = (byte) ((high & 0x000000ff00000000L)>>32);
1314            bytes[4]  = (byte) ((high & 0x00000000ff000000L)>>24);
1315            bytes[5]  = (byte) ((high & 0x0000000000ff0000L)>>16);
1316            bytes[6]  = (byte) ((high & 0x000000000000ff00L)>>8);
1317            bytes[7]  = (byte) (high & 0x00000000000000ffL);
1318            bytes[8]  = (byte) ((low & 0xff00000000000000L)>>56);
1319            bytes[9]  = (byte) ((low & 0x00ff000000000000L)>>48);
1320            bytes[10] = (byte) ((low & 0x0000ff0000000000L)>>40);
1321            bytes[11] = (byte) ((low & 0x000000ff00000000L)>>32);
1322            bytes[12] = (byte) ((low & 0x00000000ff000000L)>>24);
1323            bytes[13] = (byte) ((low & 0x0000000000ff0000L)>>16);
1324            bytes[14] = (byte) ((low & 0x000000000000ff00L)>>8);
1325            bytes[15] = (byte) (low & 0x00000000000000ffL);
1326            
1327            return bytes;
1328        }
1329    }