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