1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.api.ldap.model.schema;
21
22
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.UUID;
28
29 import org.apache.directory.api.i18n.I18n;
30 import org.apache.directory.api.ldap.model.constants.MetaSchemaConstants;
31 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
32 import org.apache.directory.api.ldap.model.entry.Attribute;
33 import org.apache.directory.api.ldap.model.entry.Entry;
34 import org.apache.directory.api.ldap.model.entry.Modification;
35 import org.apache.directory.api.ldap.model.entry.Value;
36 import org.apache.directory.api.ldap.model.exception.LdapException;
37 import org.apache.directory.api.util.Strings;
38
39
40
41
42
43
44
45 public final class SchemaUtils
46 {
47
48
49
50 private SchemaUtils()
51 {
52 }
53
54
55
56
57
58
59
60
61
62
63
64 public static Entry getTargetEntry( List<? extends Modification> mods, Entry entry )
65 throws LdapException
66 {
67 Entry targetEntry = entry.clone();
68
69 for ( Modification mod : mods )
70 {
71 String id = mod.getAttribute().getId();
72
73 switch ( mod.getOperation() )
74 {
75 case REPLACE_ATTRIBUTE:
76 targetEntry.put( mod.getAttribute() );
77 break;
78
79 case ADD_ATTRIBUTE:
80 Attribute combined = mod.getAttribute().clone();
81 Attribute toBeAdded = mod.getAttribute();
82 Attribute existing = entry.get( id );
83
84 if ( existing != null )
85 {
86 for ( Value value : existing )
87 {
88 combined.add( value );
89 }
90 }
91
92 for ( Value value : toBeAdded )
93 {
94 combined.add( value );
95 }
96
97 targetEntry.put( combined );
98 break;
99
100 case REMOVE_ATTRIBUTE:
101 Attribute toBeRemoved = mod.getAttribute();
102
103 if ( toBeRemoved.size() == 0 )
104 {
105 targetEntry.removeAttributes( id );
106 }
107 else
108 {
109 existing = targetEntry.get( id );
110
111 if ( existing != null )
112 {
113 for ( Value value : toBeRemoved )
114 {
115 existing.remove( value );
116 }
117 }
118 }
119
120 break;
121
122 case INCREMENT_ATTRIBUTE:
123
124 AttributeType attributeType = mod.getAttribute().getAttributeType();
125 String incrementStr = mod.getAttribute().getString();
126 int increment = 1;
127
128 if ( !Strings.isEmpty( incrementStr ) )
129 {
130 try
131 {
132 increment = Integer.parseInt( incrementStr );
133 }
134 catch ( NumberFormatException nfe )
135 {
136 throw new IllegalArgumentException( I18n.err( I18n.ERR_13866_MOD_INCREMENT_INVALID_VALUE,
137 attributeType.getName(), incrementStr ) );
138 }
139 }
140 Attribute modified = targetEntry.get( attributeType );
141
142 if ( !targetEntry.containsAttribute( attributeType ) )
143 {
144 throw new IllegalArgumentException( I18n.err( I18n.ERR_13867_MOD_INCREMENT_NO_ATTRIBUTE,
145 attributeType.getName() ) );
146 }
147
148 if ( !SchemaConstants.INTEGER_SYNTAX.equals( modified.getAttributeType().getSyntax().getOid() ) )
149 {
150 throw new IllegalArgumentException( I18n.err( I18n.ERR_13868_MOD_INCREMENT_NO_INT_ATTRIBUTE,
151 attributeType.getName() ) );
152 }
153 else
154 {
155 Value[] newValues = new Value[ modified.size() ];
156 int i = 0;
157
158 for ( Value value : modified )
159 {
160 int intValue = Integer.parseInt( value.getNormalized() );
161
162 if ( intValue == Integer.MAX_VALUE )
163 {
164 throw new IllegalArgumentException( I18n.err( I18n.ERR_13869_MOD_INCREMENT_OVERFLOW,
165 attributeType.getName(), intValue ) );
166 }
167
168 newValues[i++] = new Value( Integer.toString( intValue + increment ) );
169 modified.remove( value );
170 }
171
172 modified.add( newValues );
173 }
174
175 break;
176
177 default:
178 throw new IllegalStateException( I18n.err( I18n.ERR_13775_UNDEFINED_MODIFICATION_TYPE, mod.getOperation() ) );
179 }
180 }
181
182 return targetEntry;
183 }
184
185
186
187
188
189
190
191
192
193
194
195
196
197 public static StringBuilder render( StringBuilder buf, List<String> qdescrs )
198 {
199 if ( ( qdescrs == null ) || qdescrs.isEmpty() )
200 {
201 return buf;
202 }
203 else if ( qdescrs.size() == 1 )
204 {
205 buf.append( "'" ).append( qdescrs.get( 0 ) ).append( "'" );
206 }
207 else
208 {
209 buf.append( "( " );
210
211 for ( String qdescr : qdescrs )
212 {
213 buf.append( "'" ).append( qdescr ).append( "' " );
214 }
215
216 buf.append( ")" );
217 }
218
219 return buf;
220 }
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235 static StringBuilder renderQDescrs( StringBuilder buf, List<String> qdescrs )
236 {
237 if ( ( qdescrs == null ) || qdescrs.isEmpty() )
238 {
239 return buf;
240 }
241
242 if ( qdescrs.size() == 1 )
243 {
244 buf.append( '\'' ).append( qdescrs.get( 0 ) ).append( '\'' );
245 }
246 else
247 {
248 buf.append( "( " );
249
250 for ( String qdescr : qdescrs )
251 {
252 buf.append( '\'' ).append( qdescr ).append( "' " );
253 }
254
255 buf.append( ")" );
256 }
257
258 return buf;
259 }
260
261
262
263
264
265
266
267
268
269 private static StringBuilder renderQDString( StringBuilder buf, String qdString )
270 {
271 buf.append( '\'' );
272
273 for ( char c : qdString.toCharArray() )
274 {
275 switch ( c )
276 {
277 case 0x27:
278 buf.append( "\\27" );
279 break;
280
281 case 0x5C:
282 buf.append( "\\5C" );
283 break;
284
285 default:
286 buf.append( c );
287 break;
288 }
289 }
290
291 buf.append( '\'' );
292
293 return buf;
294 }
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309 public static StringBuilder render( ObjectClass[] ocs )
310 {
311 StringBuilder buf = new StringBuilder();
312
313 return render( buf, ocs );
314 }
315
316
317
318
319
320
321
322
323
324
325
326
327 public static StringBuilder render( StringBuilder buf, ObjectClass[] ocs )
328 {
329 if ( ocs == null || ocs.length == 0 )
330 {
331 return buf;
332 }
333 else if ( ocs.length == 1 )
334 {
335 buf.append( ocs[0].getName() );
336 }
337 else
338 {
339 buf.append( "( " );
340
341 for ( int ii = 0; ii < ocs.length; ii++ )
342 {
343 if ( ii + 1 < ocs.length )
344 {
345 buf.append( ocs[ii].getName() ).append( " $ " );
346 }
347 else
348 {
349 buf.append( ocs[ii].getName() );
350 }
351 }
352
353 buf.append( " )" );
354 }
355
356 return buf;
357 }
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372 public static StringBuilder render( AttributeType[] ats )
373 {
374 StringBuilder buf = new StringBuilder();
375 return render( buf, ats );
376 }
377
378
379
380
381
382
383
384
385
386
387
388
389 public static StringBuilder render( StringBuilder buf, AttributeType[] ats )
390 {
391 if ( ats == null || ats.length == 0 )
392 {
393 return buf;
394 }
395 else if ( ats.length == 1 )
396 {
397 buf.append( ats[0].getName() );
398 }
399 else
400 {
401 buf.append( "( " );
402 for ( int ii = 0; ii < ats.length; ii++ )
403 {
404 if ( ii + 1 < ats.length )
405 {
406 buf.append( ats[ii].getName() ).append( " $ " );
407 }
408 else
409 {
410 buf.append( ats[ii].getName() );
411 }
412 }
413 buf.append( " )" );
414 }
415
416 return buf;
417 }
418
419
420
421
422
423
424
425
426
427
428
429
430 public static StringBuilder render( Map<String, List<String>> extensions )
431 {
432 StringBuilder buf = new StringBuilder();
433
434 if ( extensions.isEmpty() )
435 {
436 return buf;
437 }
438
439 for ( Map.Entry<String, List<String>> entry : extensions.entrySet() )
440 {
441 buf.append( " " ).append( entry.getKey() ).append( " " );
442
443 List<String> values = entry.getValue();
444
445
446 if ( values == null || values.isEmpty() )
447 {
448 continue;
449 }
450
451
452 if ( values.size() == 1 )
453 {
454 buf.append( "'" ).append( values.get( 0 ) ).append( "' " );
455 continue;
456 }
457
458
459
460 buf.append( "( " );
461 for ( String value : values )
462 {
463 buf.append( "'" ).append( value ).append( "' " );
464 }
465 buf.append( ")" );
466 }
467
468 if ( buf.charAt( buf.length() - 1 ) != ' ' )
469 {
470 buf.append( " " );
471 }
472
473 return buf;
474 }
475
476
477
478
479
480
481
482
483
484
485 public static String render( LoadableSchemaObject description )
486 {
487 StringBuilder buf = new StringBuilder();
488 buf.append( "( " ).append( description.getOid() );
489
490 if ( description.getDescription() != null )
491 {
492 buf.append( " DESC " );
493 renderQDString( buf, description.getDescription() );
494 }
495
496 buf.append( " FQCN " ).append( description.getFqcn() );
497
498 if ( !Strings.isEmpty( description.getBytecode() ) )
499 {
500 buf.append( " BYTECODE " ).append( description.getBytecode() );
501 }
502
503 buf.append( " X-SCHEMA '" );
504 buf.append( getSchemaName( description ) );
505 buf.append( "' )" );
506
507 return buf.toString();
508 }
509
510
511 private static String getSchemaName( SchemaObject desc )
512 {
513 List<String> values = desc.getExtension( MetaSchemaConstants.X_SCHEMA_AT );
514
515 if ( values == null || values.isEmpty() )
516 {
517 return MetaSchemaConstants.SCHEMA_OTHER;
518 }
519
520 return values.get( 0 );
521 }
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538 public static String stripOptions( String attributeId )
539 {
540 int optionsPos = attributeId.indexOf( ';' );
541
542 if ( optionsPos != -1 )
543 {
544 return attributeId.substring( 0, optionsPos );
545 }
546 else
547 {
548 return attributeId;
549 }
550 }
551
552
553
554
555
556
557
558
559
560
561
562
563
564 public static Set<String> getOptions( String attributeId )
565 {
566 int optionsPos = attributeId.indexOf( ';' );
567
568 if ( optionsPos != -1 )
569 {
570 Set<String> options = new HashSet<>();
571
572 String[] res = attributeId.substring( optionsPos + 1 ).split( ";" );
573
574 for ( String option : res )
575 {
576 if ( !Strings.isEmpty( option ) )
577 {
578 options.add( option );
579 }
580 }
581
582 return options;
583 }
584 else
585 {
586 return null;
587 }
588 }
589
590
591
592
593
594
595
596 public static byte[] uuidToBytes( UUID uuid )
597 {
598 Long low = uuid.getLeastSignificantBits();
599 Long high = uuid.getMostSignificantBits();
600 byte[] bytes = new byte[16];
601
602 bytes[0] = ( byte ) ( ( high & 0xff00000000000000L ) >> 56 );
603 bytes[1] = ( byte ) ( ( high & 0x00ff000000000000L ) >> 48 );
604 bytes[2] = ( byte ) ( ( high & 0x0000ff0000000000L ) >> 40 );
605 bytes[3] = ( byte ) ( ( high & 0x000000ff00000000L ) >> 32 );
606 bytes[4] = ( byte ) ( ( high & 0x00000000ff000000L ) >> 24 );
607 bytes[5] = ( byte ) ( ( high & 0x0000000000ff0000L ) >> 16 );
608 bytes[6] = ( byte ) ( ( high & 0x000000000000ff00L ) >> 8 );
609 bytes[7] = ( byte ) ( high & 0x00000000000000ffL );
610 bytes[8] = ( byte ) ( ( low & 0xff00000000000000L ) >> 56 );
611 bytes[9] = ( byte ) ( ( low & 0x00ff000000000000L ) >> 48 );
612 bytes[10] = ( byte ) ( ( low & 0x0000ff0000000000L ) >> 40 );
613 bytes[11] = ( byte ) ( ( low & 0x000000ff00000000L ) >> 32 );
614 bytes[12] = ( byte ) ( ( low & 0x00000000ff000000L ) >> 24 );
615 bytes[13] = ( byte ) ( ( low & 0x0000000000ff0000L ) >> 16 );
616 bytes[14] = ( byte ) ( ( low & 0x000000000000ff00L ) >> 8 );
617 bytes[15] = ( byte ) ( low & 0x00000000000000ffL );
618
619 return bytes;
620 }
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647 public static boolean isAttributeNameValid( String attributeName )
648 {
649 if ( Strings.isEmpty( attributeName ) )
650 {
651 return false;
652 }
653
654
655 boolean descr;
656 boolean zero = false;
657 boolean dot = false;
658
659 char c = attributeName.charAt( 0 );
660
661 if ( ( ( c >= 'a' ) && ( c <= 'z' ) ) || ( ( c >= 'A' ) && ( c <= 'Z' ) ) )
662 {
663 descr = true;
664 }
665 else if ( ( c >= '0' ) && ( c <= '9' ) )
666 {
667 descr = false;
668
669 zero = c == '0';
670 }
671 else
672 {
673 return false;
674 }
675
676 for ( int i = 1; i < attributeName.length(); i++ )
677 {
678 c = attributeName.charAt( i );
679
680 if ( descr )
681 {
682
683 if ( ( ( c < 'a' ) || ( c > 'z' ) )
684 && ( ( c < 'A' ) || ( c > 'Z' ) )
685 && ( ( c < '0' ) || ( c > '9' ) )
686 && ( c != '-' )
687 && ( c != '_' ) )
688 {
689 return false;
690 }
691 }
692 else
693 {
694
695 if ( c == '.' )
696 {
697
698 if ( dot )
699 {
700 return false;
701 }
702
703 dot = true;
704 zero = false;
705 }
706 else if ( ( c >= '0' ) && ( c <= '9' ) )
707 {
708 dot = false;
709
710 if ( zero )
711 {
712
713 return false;
714 }
715 else if ( c == '0' )
716 {
717 zero = true;
718 }
719 }
720 else
721 {
722
723 return false;
724 }
725 }
726 }
727
728 return true;
729 }
730 }