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