1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.directory.api.ldap.model.name;
22
23
24 import java.io.Externalizable;
25 import java.io.IOException;
26 import java.io.ObjectInput;
27 import java.io.ObjectOutput;
28 import java.util.ArrayList;
29 import java.util.Iterator;
30 import java.util.List;
31
32 import org.apache.commons.collections4.list.UnmodifiableList;
33 import org.apache.directory.api.i18n.I18n;
34 import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
35 import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
36 import org.apache.directory.api.ldap.model.schema.SchemaManager;
37 import org.apache.directory.api.util.Strings;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 public class Dn implements Iterable<Rdn>, Externalizable
63 {
64
65 protected static final Logger LOG = LoggerFactory.getLogger( Dn.class );
66
67
68
69
70
71
72
73
74 private static final long serialVersionUID = 1L;
75
76
77 public static final int NOT_EQUAL = -1;
78
79
80 public static final int EQUAL = 0;
81
82
83
84
85
86
87
88
89
90
91
92
93
94 protected transient List<Rdn> rdns = new ArrayList<>( 5 );
95
96
97 private String upName;
98
99
100 private String normName;
101
102
103 public static final Dn EMPTY_DN = new Dn();
104
105
106 public static final Dn ROOT_DSE = new Dn();
107
108
109 private transient SchemaManager schemaManager;
110
111
112 private static final boolean LEFT = true;
113 private static final boolean RIGHT = false;
114
115
116
117
118 private final class RdnIterator implements Iterator<Rdn>
119 {
120
121 int index;
122
123
124 private RdnIterator()
125 {
126 index = rdns != null ? rdns.size() - 1 : -1;
127 }
128
129
130
131
132
133 @Override
134 public boolean hasNext()
135 {
136 return index >= 0;
137 }
138
139
140
141
142
143 @Override
144 public Rdn next()
145 {
146 return index >= 0 ? rdns.get( index-- ) : null;
147 }
148
149
150
151
152
153 @Override
154 public void remove()
155 {
156
157 }
158 }
159
160
161
162
163
164 public Dn()
165 {
166 this( ( SchemaManager ) null );
167 }
168
169
170
171
172
173
174
175 public Dn( SchemaManager schemaManager )
176 {
177 this.schemaManager = schemaManager;
178 upName = "";
179 normName = "";
180 }
181
182
183
184
185
186
187
188
189
190 public Dn( SchemaManager schemaManager, Dn dn ) throws LdapInvalidDnException
191 {
192 this.schemaManager = schemaManager;
193
194 if ( dn == null )
195 {
196 return;
197 }
198
199 for ( Rdn rdn : dn.rdns )
200 {
201 this.rdns.add( new Rdn( schemaManager, rdn ) );
202 }
203
204 upName = toUpName();
205 }
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230 public Dn( String... upRdns ) throws LdapInvalidDnException
231 {
232 this( null, upRdns );
233 }
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259 public Dn( SchemaManager schemaManager, String... upRdns ) throws LdapInvalidDnException
260 {
261 StringBuilder sbUpName = new StringBuilder();
262 boolean valueExpected = false;
263 boolean isFirst = true;
264 this.schemaManager = schemaManager;
265
266 for ( String upRdn : upRdns )
267 {
268 if ( Strings.isEmpty( upRdn ) )
269 {
270 continue;
271 }
272
273 if ( isFirst )
274 {
275 isFirst = false;
276 }
277 else if ( !valueExpected )
278 {
279 sbUpName.append( ',' );
280 }
281
282 if ( !valueExpected )
283 {
284 sbUpName.append( upRdn );
285
286 if ( upRdn.indexOf( '=' ) == -1 )
287 {
288 valueExpected = true;
289 }
290 }
291 else
292 {
293 sbUpName.append( "=" ).append( upRdn );
294
295 valueExpected = false;
296 }
297 }
298
299 if ( !isFirst && valueExpected )
300 {
301 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX, I18n.err( I18n.ERR_13611_VALUE_MISSING_ON_RDN ) );
302 }
303
304
305
306 upName = sbUpName.toString();
307
308 try
309 {
310 normName = parseInternal( schemaManager, upName, rdns );
311 }
312 catch ( LdapInvalidDnException e )
313 {
314 if ( schemaManager == null || !schemaManager.isRelaxed() )
315 {
316 throw e;
317 }
318
319
320
321
322 }
323 }
324
325
326
327
328
329
330
331
332 public Dn( Rdn... rdns ) throws LdapInvalidDnException
333 {
334 if ( rdns == null )
335 {
336 return;
337 }
338
339 for ( Rdn rdn : rdns )
340 {
341 this.rdns.add( rdn );
342 }
343
344 toUpName();
345 }
346
347
348
349
350
351
352
353
354
355 public Dn( Rdn rdn, Dn dn ) throws LdapInvalidDnException
356 {
357 if ( ( dn == null ) || ( rdn == null ) )
358 {
359 throw new IllegalArgumentException( I18n.err( I18n.ERR_13622_DN_OR_RDN_NULL ) );
360 }
361
362 for ( Rdn rdnParent : dn )
363 {
364 rdns.add( 0, rdnParent );
365 }
366
367 rdns.add( 0, rdn );
368
369 toUpName();
370 }
371
372
373
374
375
376
377
378
379
380 public Dn( SchemaManager schemaManager, Rdn... rdns ) throws LdapInvalidDnException
381 {
382 this.schemaManager = schemaManager;
383
384 if ( rdns == null )
385 {
386 return;
387 }
388
389 for ( Rdn rdn : rdns )
390 {
391 if ( rdn.isSchemaAware() )
392 {
393 this.rdns.add( rdn );
394 }
395 else
396 {
397 this.rdns.add( new Rdn( schemaManager, rdn ) );
398 }
399 }
400
401 toUpName();
402 }
403
404
405
406
407
408
409
410 public SchemaManager getSchemaManager()
411 {
412 return schemaManager;
413 }
414
415
416
417
418
419
420
421 private String toUpName()
422 {
423 if ( rdns.isEmpty() )
424 {
425 upName = "";
426 normName = "";
427 }
428 else
429 {
430 StringBuilder sbUpName = new StringBuilder();
431 StringBuilder sbNormName = new StringBuilder();
432 boolean isFirst = true;
433
434 for ( Rdn rdn : rdns )
435 {
436 if ( isFirst )
437 {
438 isFirst = false;
439 }
440 else
441 {
442 sbUpName.append( ',' );
443 sbNormName.append( ',' );
444 }
445
446 sbUpName.append( rdn.getName() );
447 sbNormName.append( rdn.getNormName() );
448 }
449
450 upName = sbUpName.toString();
451 normName = sbNormName.toString();
452 }
453
454 return upName;
455 }
456
457
458
459
460
461
462
463
464 @Override
465 public int hashCode()
466 {
467 int result = 37;
468
469 for ( Rdn rdn : rdns )
470 {
471 result = result * 17 + rdn.hashCode();
472 }
473
474 return result;
475 }
476
477
478
479
480
481
482
483 public String getName()
484 {
485 return upName == null ? "" : upName;
486 }
487
488
489
490
491
492
493
494 public String getNormName()
495 {
496 return normName == null ? "" : normName;
497 }
498
499
500
501
502
503 public String getEscaped()
504 {
505 StringBuilder sb = new StringBuilder();
506
507 boolean isFirst = true;
508
509 for ( Rdn rdn : rdns )
510 {
511 if ( isFirst )
512 {
513 isFirst = false;
514 }
515 else
516 {
517 sb.append( ',' );
518 }
519
520 sb.append( rdn.getEscaped() );
521 }
522
523 return sb.toString();
524 }
525
526
527
528
529
530
531
532
533
534 void setUpName( String upName )
535 {
536 this.upName = upName;
537 }
538
539
540
541
542
543
544
545
546
547 void setNormName( String normName )
548 {
549 this.normName = normName;
550 }
551
552
553
554
555
556
557 public int size()
558 {
559 return rdns.size();
560 }
561
562
563
564
565
566
567
568
569
570
571 public boolean isAncestorOf( String dn )
572 {
573 try
574 {
575 return isAncestorOf( new Dn( dn ) );
576 }
577 catch ( LdapInvalidDnException lide )
578 {
579 return false;
580 }
581 }
582
583
584
585
586
587
588
589
590
591
592 public boolean isAncestorOf( Dn dn )
593 {
594 if ( dn == null )
595 {
596 return false;
597 }
598
599 return dn.isDescendantOf( this );
600 }
601
602
603
604
605
606
607
608
609
610
611 public boolean isDescendantOf( String dn )
612 {
613 try
614 {
615 return isDescendantOf( new Dn( schemaManager, dn ) );
616 }
617 catch ( LdapInvalidDnException lide )
618 {
619 return false;
620 }
621 }
622
623
624
625
626
627
628
629
630
631
632 public boolean isDescendantOf( Dn dn )
633 {
634 if ( ( dn == null ) || dn.isRootDse() )
635 {
636 return true;
637 }
638
639 if ( dn.size() > size() )
640 {
641
642 return false;
643 }
644
645
646
647
648 for ( int i = dn.size() - 1; i >= 0; i-- )
649 {
650 Rdn nameRdn = dn.rdns.get( dn.rdns.size() - i - 1 );
651 Rdn ldapRdn = rdns.get( rdns.size() - i - 1 );
652
653 if ( !nameRdn.equals( ldapRdn ) )
654 {
655 return false;
656 }
657 }
658
659 return true;
660 }
661
662
663
664
665
666
667
668 public boolean isEmpty()
669 {
670 return rdns.isEmpty();
671 }
672
673
674
675
676
677
678
679 public boolean isRootDse()
680 {
681 return rdns.isEmpty();
682 }
683
684
685
686
687
688
689
690
691
692
693
694 public Rdn getRdn( int posn )
695 {
696 if ( rdns.isEmpty() )
697 {
698 return null;
699 }
700
701 if ( ( posn < 0 ) || ( posn >= rdns.size() ) )
702 {
703 throw new IllegalArgumentException( I18n.err( I18n.ERR_13623_INVALID_POSITION, posn ) );
704 }
705
706 return rdns.get( posn );
707 }
708
709
710
711
712
713
714
715 public Rdn getRdn()
716 {
717 if ( isNullOrEmpty( this ) )
718 {
719 return Rdn.EMPTY_RDN;
720 }
721
722 return rdns.get( 0 );
723 }
724
725
726
727
728
729
730
731 public List<Rdn> getRdns()
732 {
733 return UnmodifiableList.unmodifiableList( rdns );
734 }
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755 public Dn getDescendantOf( String ancestor ) throws LdapInvalidDnException
756 {
757 return getDescendantOf( new Dn( schemaManager, ancestor ) );
758 }
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779 public Dn getDescendantOf( Dn ancestor ) throws LdapInvalidDnException
780 {
781 if ( ( ancestor == null ) || ( ancestor.size() == 0 ) )
782 {
783 return this;
784 }
785
786 if ( rdns.isEmpty() )
787 {
788 return EMPTY_DN;
789 }
790
791 int length = ancestor.size();
792
793 if ( length > rdns.size() )
794 {
795 String message = I18n.err( I18n.ERR_13612_POSITION_NOT_IN_RANGE, length, rdns.size() );
796 LOG.error( message );
797 throw new ArrayIndexOutOfBoundsException( message );
798 }
799
800 Dn newDn = new Dn( schemaManager );
801 List<Rdn> rdnsAncestor = ancestor.getRdns();
802
803 for ( int i = 0; i < ancestor.size(); i++ )
804 {
805 Rdn rdn = rdns.get( size() - 1 - i );
806 Rdn rdnDescendant = rdnsAncestor.get( ancestor.size() - 1 - i );
807
808 if ( !rdn.equals( rdnDescendant ) )
809 {
810 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX );
811 }
812 }
813
814
815 if ( rdns.get( 0 ).equals( rdnsAncestor.get( 0 ) ) )
816 {
817 return newDn;
818 }
819
820 for ( int i = 0; i < rdns.size() - length; i++ )
821 {
822 newDn.rdns.add( rdns.get( i ) );
823 }
824
825 newDn.toUpName();
826
827 return newDn;
828 }
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849 public Dn getAncestorOf( String descendant ) throws LdapInvalidDnException
850 {
851 return getAncestorOf( new Dn( schemaManager, descendant ) );
852 }
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873 public Dn getAncestorOf( Dn descendant ) throws LdapInvalidDnException
874 {
875 int length = descendant.size();
876
877 if ( ( descendant == null ) || ( length == 0 ) )
878 {
879 return this;
880 }
881
882 if ( rdns.isEmpty() || length == rdns.size() )
883 {
884 return EMPTY_DN;
885 }
886
887 if ( length > rdns.size() )
888 {
889 String message = I18n.err( I18n.ERR_13612_POSITION_NOT_IN_RANGE, length, rdns.size() );
890 LOG.error( message );
891 throw new ArrayIndexOutOfBoundsException( message );
892 }
893
894 Dn newDn = new Dn( schemaManager );
895 List<Rdn> rdnsDescendant = descendant.getRdns();
896
897 for ( int i = 0; i < descendant.size(); i++ )
898 {
899 Rdn rdn = rdns.get( i );
900 Rdn rdnDescendant = rdnsDescendant.get( i );
901
902 if ( !rdn.equals( rdnDescendant ) )
903 {
904 throw new LdapInvalidDnException( ResultCodeEnum.INVALID_DN_SYNTAX );
905 }
906 }
907
908 for ( int i = length; i < rdns.size(); i++ )
909 {
910 newDn.rdns.add( rdns.get( i ) );
911 }
912
913 newDn.toUpName();
914
915
916
917 return newDn;
918 }
919
920
921
922
923
924
925
926
927
928
929
930
931 public Dn add( Dn rdns ) throws LdapInvalidDnException
932 {
933 if ( ( rdns == null ) || ( rdns.size() == 0 ) )
934 {
935 return this;
936 }
937
938 Dn clonedDn = copy();
939
940
941 clonedDn.rdns.addAll( 0, rdns.rdns );
942
943
944 if ( clonedDn.isSchemaAware() && rdns.isSchemaAware() )
945 {
946 if ( clonedDn.size() != 0 )
947 {
948 clonedDn.upName = rdns.getName() + "," + upName;
949 }
950 }
951 else
952 {
953 clonedDn.toUpName();
954 }
955
956 return clonedDn;
957 }
958
959
960
961
962
963
964
965
966
967
968
969
970 public Dn add( String rdn ) throws LdapInvalidDnException
971 {
972 if ( rdn.length() == 0 )
973 {
974 return this;
975 }
976
977 Dn clonedDn = copy();
978
979
980 Rdn newRdn = new Rdn( schemaManager, rdn );
981
982 clonedDn.rdns.add( 0, newRdn );
983
984 clonedDn.toUpName();
985
986 return clonedDn;
987 }
988
989
990
991
992
993
994
995
996
997 public Dn add( Rdn newRdn ) throws LdapInvalidDnException
998 {
999 if ( ( newRdn == null ) || ( newRdn.size() == 0 ) )
1000 {
1001 return this;
1002 }
1003
1004 Dn clonedDn = copy();
1005
1006 clonedDn.rdns.add( 0, new Rdn( schemaManager, newRdn ) );
1007 clonedDn.toUpName();
1008
1009 return clonedDn;
1010 }
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020 public Dn getParent()
1021 {
1022 if ( isNullOrEmpty( this ) )
1023 {
1024 return this;
1025 }
1026
1027 int posn = rdns.size() - 1;
1028
1029 Dn newDn = new Dn( schemaManager );
1030
1031 for ( int i = rdns.size() - posn; i < rdns.size(); i++ )
1032 {
1033 newDn.rdns.add( rdns.get( i ) );
1034 }
1035
1036 newDn.toUpName();
1037
1038 return newDn;
1039 }
1040
1041
1042 private String removeUpName( String removedUpName, boolean fromLeft )
1043 {
1044 int removedSize = removedUpName.length();
1045
1046 if ( fromLeft )
1047 {
1048 for ( int i = removedSize; i < upName.length(); i++ )
1049 {
1050 if ( upName.charAt( i ) == ',' )
1051 {
1052 return upName.substring( i + 1 );
1053 }
1054 }
1055 }
1056 else
1057 {
1058 for ( int i = upName.length() - removedSize; i > 0; i-- )
1059 {
1060 if ( upName.charAt( i ) == ',' )
1061 {
1062 return upName.substring( 0, i - 1 );
1063 }
1064 }
1065 }
1066
1067
1068 return Strings.EMPTY_STRING;
1069 }
1070
1071
1072
1073
1074
1075
1076
1077 private Dn copy()
1078 {
1079 Dn dn = new Dn( schemaManager );
1080 dn.rdns = new ArrayList<>();
1081
1082 for ( Rdn rdn : rdns )
1083 {
1084 dn.rdns.add( rdn );
1085 }
1086
1087 return dn;
1088 }
1089
1090
1091
1092
1093
1094
1095 @Override
1096 public boolean equals( Object obj )
1097 {
1098 Dn other;
1099
1100 if ( obj instanceof String )
1101 {
1102 try
1103 {
1104 other = new Dn( schemaManager, ( String ) obj );
1105 }
1106 catch ( LdapInvalidDnException e )
1107 {
1108 return false;
1109 }
1110 }
1111 else if ( obj instanceof Dn )
1112 {
1113 other = ( Dn ) obj;
1114 }
1115 else
1116 {
1117 return false;
1118 }
1119
1120 if ( other.size() != this.size() )
1121 {
1122 return false;
1123 }
1124
1125
1126 if ( isSchemaAware() )
1127 {
1128 if ( normName == null )
1129 {
1130
1131 return other.normName == null;
1132 }
1133 return normName.equals( other.normName );
1134 }
1135
1136 for ( int i = 0; i < this.size(); i++ )
1137 {
1138 if ( !other.rdns.get( i ).equals( rdns.get( i ) ) )
1139 {
1140 return false;
1141 }
1142 }
1143
1144
1145 return true;
1146 }
1147
1148
1149
1150
1151
1152
1153
1154 public boolean isSchemaAware()
1155 {
1156 return schemaManager != null;
1157 }
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179 @Override
1180 public Iterator<Rdn> iterator()
1181 {
1182 return new RdnIterator();
1183 }
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193 public static boolean isNullOrEmpty( Dn dn )
1194 {
1195 return ( dn == null ) || dn.isEmpty();
1196 }
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206 public static boolean isValid( String name )
1207 {
1208 Dn dn = new Dn();
1209
1210 try
1211 {
1212 parseInternal( null, name, dn.rdns );
1213 return true;
1214 }
1215 catch ( LdapInvalidDnException e )
1216 {
1217 return false;
1218 }
1219 }
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230 public static boolean isValid( SchemaManager schemaManager, String name )
1231 {
1232 Dn dn = new Dn();
1233
1234 try
1235 {
1236 parseInternal( schemaManager, name, dn.rdns );
1237 return true;
1238 }
1239 catch ( LdapInvalidDnException e )
1240 {
1241 return false;
1242 }
1243 }
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255 private static String parseInternal( SchemaManager schemaManager, String name, List<Rdn> rdns ) throws LdapInvalidDnException
1256 {
1257 try
1258 {
1259 return FastDnParser.parseDn( schemaManager, name, rdns );
1260 }
1261 catch ( TooComplexDnException e )
1262 {
1263 rdns.clear();
1264 return new ComplexDnParser().parseDn( schemaManager, name, rdns );
1265 }
1266 }
1267
1268
1269
1270
1271
1272 @Override
1273 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
1274 {
1275
1276 upName = in.readUTF();
1277
1278
1279 int nbRdns = in.readInt();
1280
1281 rdns = new ArrayList<>( nbRdns );
1282
1283 for ( int i = 0; i < nbRdns; i++ )
1284 {
1285 Rdn rdn = new Rdn( schemaManager );
1286 rdn.readExternal( in );
1287 rdns.add( rdn );
1288 }
1289
1290 toUpName();
1291 }
1292
1293
1294
1295
1296
1297 @Override
1298 public void writeExternal( ObjectOutput out ) throws IOException
1299 {
1300 if ( upName == null )
1301 {
1302 String message = I18n.err( I18n.ERR_13624_CANNOT_SERIALIZE_NULL_DN );
1303 LOG.error( message );
1304 throw new IOException( message );
1305 }
1306
1307
1308 out.writeUTF( upName );
1309
1310
1311
1312 out.writeInt( size() );
1313
1314
1315 for ( Rdn rdn : rdns )
1316 {
1317 rdn.writeExternal( out );
1318 }
1319
1320 out.flush();
1321 }
1322
1323
1324
1325
1326
1327
1328
1329
1330 @Override
1331 public String toString()
1332 {
1333 return getName();
1334 }
1335 }