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