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 "NAME" SP qdescrs ] ; short names (descriptors)
418 * [ SP "DESC" SP qdstring ] ; description
419 * [ SP "OBSOLETE" ] ; not active
420 * [ SP "SUP" SP oids ] ; superior object classes
421 * [ SP kind ] ; kind of class
422 * [ SP "MUST" SP oids ] ; attribute types
423 * [ SP "MAY" SP oids ] ; attribute types
424 * extensions WSP RPAREN
425 *
426 * kind = "ABSTRACT" / "STRUCTURAL" / "AUXILIARY"
427 *
428 * where:
429 * <numericoid> is object identifier assigned to this object class;
430 * NAME <qdescrs> are short names (descriptors) identifying this object
431 * class;
432 * DESC <qdstring> is a short descriptive string;
433 * OBSOLETE indicates this object class is not active;
434 * SUP <oids> 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 * <extensions> 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 "NAME" SP qdescrs ] ; short names (descriptors)
527 * [ SP "DESC" SP qdstring ] ; description
528 * [ SP "OBSOLETE" ] ; not active
529 * [ SP "SUP" SP oid ] ; supertype
530 * [ SP "EQUALITY" SP oid ] ; equality matching rule
531 * [ SP "ORDERING" SP oid ] ; ordering matching rule
532 * [ SP "SUBSTR" SP oid ] ; substrings matching rule
533 * [ SP "SYNTAX" SP noidlen ] ; value syntax
534 * [ SP "SINGLE-VALUE" ] ; single-value
535 * [ SP "COLLECTIVE" ] ; collective
536 * [ SP "NO-USER-MODIFICATION" ] ; not user modifiable
537 * [ SP "USAGE" SP usage ] ; usage
538 * extensions WSP RPAREN ; extensions
539 *
540 * usage = "userApplications" / ; user
541 * "directoryOperation" / ; directory operational
542 * "distributedOperation" / ; DSA-shared operational
543 * "dSAOperation" ; DSA-specific operational
544 *
545 * where:
546 * <numericoid> is object identifier assigned to this attribute type;
547 * NAME <qdescrs> are short names (descriptors) identifying this
548 * attribute type;
549 * DESC <qdstring> 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 * <extensions> 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 "NAME" SP qdescrs ] ; short names (descriptors)
672 * [ SP "DESC" SP qdstring ] ; description
673 * [ SP "OBSOLETE" ] ; not active
674 * [ SP "SUP" SP oid ] ; supertype
675 * [ SP "EQUALITY" SP oid ] ; equality matching rule
676 * [ SP "ORDERING" SP oid ] ; ordering matching rule
677 * [ SP "SUBSTR" SP oid ] ; substrings matching rule
678 * [ SP "SYNTAX" SP noidlen ] ; value syntax
679 * [ SP "SINGLE-VALUE" ] ; single-value
680 * [ SP "COLLECTIVE" ] ; collective
681 * [ SP "NO-USER-MODIFICATION" ] ; not user modifiable
682 * [ SP "USAGE" SP usage ] ; usage
683 * extensions WSP RPAREN ; extensions
684 *
685 * usage = "userApplications" / ; user
686 * "directoryOperation" / ; directory operational
687 * "distributedOperation" / ; DSA-shared operational
688 * "dSAOperation" ; DSA-specific operational
689 *
690 * where:
691 * <numericoid> is object identifier assigned to this attribute type;
692 * NAME <qdescrs> are short names (descriptors) identifying this
693 * attribute type;
694 * DESC <qdstring> 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 * <extensions> 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 "NAME" SP qdescrs ] ; short names (descriptors)
871 * [ SP "DESC" SP qdstring ] ; description
872 * [ SP "OBSOLETE" ] ; not active
873 * SP "SYNTAX" SP numericoid ; assertion syntax
874 * extensions WSP RPAREN ; extensions
875 *
876 * where:
877 * <numericoid> is object identifier assigned to this matching rule;
878 * NAME <qdescrs> are short names (descriptors) identifying this
879 * matching rule;
880 * DESC <qdstring> 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 * <extensions> 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 "DESC" SP qdstring ] ; description
943 * extensions WSP RPAREN ; extensions
944 *
945 * where:
946 * <numericoid> is the object identifier assigned to this LDAP syntax;
947 * DESC <qdstring> is a short descriptive string; and
948 * <extensions> 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 }