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