1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.bremersee.ldaptive;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.LinkedHashSet;
24 import java.util.List;
25 import java.util.Objects;
26 import java.util.Optional;
27 import java.util.Set;
28 import java.util.stream.Stream;
29 import org.ldaptive.AttributeModification;
30 import org.ldaptive.AttributeModification.Type;
31 import org.ldaptive.LdapAttribute;
32 import org.ldaptive.LdapEntry;
33 import org.ldaptive.ModifyRequest;
34 import org.ldaptive.beans.LdapEntryMapper;
35 import org.ldaptive.dn.Dn;
36 import org.ldaptive.dn.NameValue;
37 import org.ldaptive.dn.RDn;
38 import org.ldaptive.transcode.ValueTranscoder;
39
40
41
42
43
44
45
46 public interface LdaptiveEntryMapper<T> extends LdapEntryMapper<T> {
47
48
49
50
51
52
53
54 String[] getObjectClasses();
55
56
57
58
59
60
61 default String[] getMappedAttributeNames() {
62 return new String[0];
63 }
64
65
66
67
68
69
70 default String[] getBinaryAttributeNames() {
71 return new String[0];
72 }
73
74 @Override
75 String mapDn(T domainObject);
76
77
78
79
80
81
82
83 T map(LdapEntry ldapEntry);
84
85 @Override
86 void map(LdapEntry source, T destination);
87
88 @Override
89 default void map(T source, LdapEntry destination) {
90 mapAndComputeModifications(source, destination);
91 }
92
93
94
95
96
97
98
99
100
101 AttributeModification[] mapAndComputeModifications(
102 T source,
103 LdapEntry destination);
104
105
106
107
108
109
110
111
112 default Optional<ModifyRequest> mapAndComputeModifyRequest(
113 T source,
114 LdapEntry destination) {
115
116 return Optional.ofNullable(mapAndComputeModifications(source, destination))
117 .filter(mods -> mods.length > 0)
118 .map(mods -> new ModifyRequest(destination.getDn(), mods));
119 }
120
121
122
123
124
125
126
127
128
129
130
131 static <T> T getAttributeValue(
132 LdapEntry ldapEntry,
133 String name,
134 ValueTranscoder<T> valueTranscoder,
135 T defaultValue) {
136 LdapAttribute attr = ldapEntry == null ? null : ldapEntry.getAttribute(name);
137 T value = attr != null ? attr.getValue(valueTranscoder.decoder()) : null;
138 return value != null ? value : defaultValue;
139 }
140
141
142
143
144
145
146
147
148
149
150 static <T> T getAttributeValue(
151 LdapEntry ldapEntry,
152 LdaptiveAttribute<T> attribute,
153 T defaultValue) {
154 return getAttributeValue(
155 ldapEntry,
156 attribute.getName(),
157 attribute.getValueTranscoder(),
158 defaultValue);
159 }
160
161
162
163
164
165
166
167
168
169
170 static <T> Collection<T> getAttributeValues(
171 LdapEntry ldapEntry,
172 String name,
173 ValueTranscoder<T> valueTranscoder) {
174 LdapAttribute attr = ldapEntry == null ? null : ldapEntry.getAttribute(name);
175 Collection<T> values = attr != null ? attr.getValues(valueTranscoder.decoder()) : null;
176 return values != null ? values : new ArrayList<>();
177 }
178
179
180
181
182
183
184
185
186
187 static <T> Collection<T> getAttributeValues(
188 LdapEntry ldapEntry,
189 LdaptiveAttribute<T> attribute) {
190 return getAttributeValues(ldapEntry, attribute.getName(), attribute.getValueTranscoder());
191 }
192
193
194
195
196
197
198
199
200
201
202 static <T> Set<T> getAttributeValuesAsSet(
203 LdapEntry ldapEntry,
204 String name,
205 ValueTranscoder<T> valueTranscoder) {
206 return new LinkedHashSet<>(getAttributeValues(ldapEntry, name, valueTranscoder));
207 }
208
209
210
211
212
213
214
215
216
217 static <T> Set<T> getAttributeValuesAsSet(
218 LdapEntry ldapEntry,
219 LdaptiveAttribute<T> attribute) {
220 return getAttributeValuesAsSet(ldapEntry, attribute.getName(), attribute.getValueTranscoder());
221 }
222
223
224
225
226
227
228
229
230
231
232 static <T> List<T> getAttributeValuesAsList(
233 LdapEntry ldapEntry,
234 String name,
235 ValueTranscoder<T> valueTranscoder) {
236 return new ArrayList<>(getAttributeValues(ldapEntry, name, valueTranscoder));
237 }
238
239
240
241
242
243
244
245
246
247 static <T> List<T> getAttributeValuesAsList(
248 LdapEntry ldapEntry,
249 LdaptiveAttribute<T> attribute) {
250 return getAttributeValuesAsList(ldapEntry, attribute.getName(), attribute.getValueTranscoder());
251 }
252
253
254
255
256
257
258
259
260
261
262
263
264 static <T> void setAttribute(
265 LdapEntry ldapEntry,
266 String name,
267 T value,
268 boolean isBinary,
269 ValueTranscoder<T> valueTranscoder,
270 List<AttributeModification> modifications) {
271
272 setAttributes(
273 ldapEntry,
274 name,
275 value != null ? Collections.singleton(value) : null,
276 isBinary,
277 valueTranscoder,
278 modifications);
279 }
280
281
282
283
284
285
286
287
288
289
290 static <T> void setAttribute(
291 LdapEntry ldapEntry,
292 LdaptiveAttribute<T> attribute,
293 T value,
294 List<AttributeModification> modifications) {
295
296 setAttributes(
297 ldapEntry,
298 attribute.getName(),
299 value != null ? Collections.singleton(value) : null,
300 attribute.isBinary(),
301 attribute.getValueTranscoder(),
302 modifications);
303 }
304
305
306
307
308
309
310
311
312
313
314
315
316 static <T> void setAttributes(
317 LdapEntry ldapEntry,
318 String name,
319 Collection<T> values,
320 boolean isBinary,
321 ValueTranscoder<T> valueTranscoder,
322 List<AttributeModification> modifications) {
323
324 Collection<T> realValues = Stream.ofNullable(values)
325 .flatMap(Collection::stream)
326 .filter(value -> {
327 if (value instanceof CharSequence cs) {
328 return !cs.isEmpty();
329 }
330 return value != null;
331 })
332 .toList();
333 LdapAttribute attr = ldapEntry.getAttribute(name);
334 if (attr == null && !realValues.isEmpty()) {
335 addAttributes(ldapEntry, name, realValues, isBinary, valueTranscoder, modifications);
336 } else if (attr != null) {
337 if (realValues.isEmpty()) {
338 ldapEntry.removeAttribute(name);
339 modifications.add(
340 new AttributeModification(
341 Type.DELETE,
342 attr));
343 } else if (areNotEqual(realValues, attr.getValues(valueTranscoder.decoder()))) {
344 LdapAttribute newAttr = new LdapAttribute();
345 newAttr.setBinary(isBinary);
346 newAttr.setName(name);
347 newAttr.addValues(valueTranscoder.encoder(), realValues);
348 ldapEntry.addAttributes(newAttr);
349 modifications.add(
350 new AttributeModification(
351 Type.REPLACE,
352 newAttr));
353 }
354 }
355 }
356
357
358
359
360
361
362
363
364
365
366 static <T> void setAttributes(
367 LdapEntry ldapEntry,
368 LdaptiveAttribute<T> attribute,
369 Collection<T> values,
370 List<AttributeModification> modifications) {
371 setAttributes(ldapEntry, attribute.getName(), values, attribute.isBinary(),
372 attribute.getValueTranscoder(), modifications);
373 }
374
375 private static boolean areNotEqual(Collection<?> newValues, Collection<?> existingValues) {
376 if (newValues.size() != existingValues.size()) {
377 return true;
378 }
379 List<?> newValueList = new ArrayList<>(newValues);
380 List<?> existingValueList = new ArrayList<>(existingValues);
381 for (int i = 0; i < newValueList.size(); i++) {
382 Object newValue = newValueList.get(i);
383 Object existingValue = existingValueList.get(i);
384 if (newValue instanceof byte[] n && existingValue instanceof byte[] e) {
385 if (!Arrays.equals(n, e)) {
386 return true;
387 }
388 } else if (!Objects.equals(newValue, existingValue)) {
389 return true;
390 }
391 }
392 return false;
393 }
394
395
396
397
398
399
400
401
402
403
404
405
406 static <T> void addAttribute(
407 LdapEntry ldapEntry,
408 String name,
409 T value,
410 boolean isBinary,
411 ValueTranscoder<T> valueTranscoder,
412 List<AttributeModification> modifications) {
413 addAttributes(
414 ldapEntry,
415 name,
416 value != null ? Collections.singleton(value) : null,
417 isBinary,
418 valueTranscoder,
419 modifications);
420 }
421
422
423
424
425
426
427
428
429
430
431 static <T> void addAttribute(
432 LdapEntry ldapEntry,
433 LdaptiveAttribute<T> attribute,
434 T value,
435 List<AttributeModification> modifications) {
436 addAttributes(
437 ldapEntry,
438 attribute.getName(),
439 value != null ? Collections.singleton(value) : null,
440 attribute.isBinary(),
441 attribute.getValueTranscoder(),
442 modifications);
443 }
444
445
446
447
448
449
450
451
452
453
454
455
456 static <T> void addAttributes(
457 LdapEntry ldapEntry,
458 String name,
459 Collection<T> values,
460 boolean isBinary,
461 ValueTranscoder<T> valueTranscoder,
462 List<AttributeModification> modifications) {
463 Collection<T> realValues = Stream.ofNullable(values)
464 .flatMap(Collection::stream)
465 .filter(value -> {
466 if (value instanceof CharSequence cs) {
467 return !cs.isEmpty();
468 }
469 return value != null;
470 })
471 .toList();
472 if (realValues.isEmpty()) {
473 return;
474 }
475 LdapAttribute attr = ldapEntry.getAttribute(name);
476 if (attr == null) {
477 LdapAttribute newAttr = new LdapAttribute();
478 newAttr.setBinary(isBinary);
479 newAttr.setName(name);
480 newAttr.addValues(valueTranscoder.encoder(), realValues);
481 ldapEntry.addAttributes(newAttr);
482 modifications.add(
483 new AttributeModification(
484 Type.ADD,
485 newAttr));
486 } else {
487 List<T> newValues = new ArrayList<>(
488 getAttributeValues(ldapEntry, name, valueTranscoder));
489 newValues.addAll(realValues);
490 setAttributes(ldapEntry, name, newValues, attr.isBinary(), valueTranscoder, modifications);
491 }
492 }
493
494
495
496
497
498
499
500
501
502
503 static <T> void addAttributes(
504 LdapEntry ldapEntry,
505 LdaptiveAttribute<T> attribute,
506 Collection<T> values,
507 List<AttributeModification> modifications) {
508 addAttributes(ldapEntry, attribute.getName(), values, attribute.isBinary(),
509 attribute.getValueTranscoder(), modifications);
510 }
511
512
513
514
515
516
517
518
519 static void removeAttribute(
520 LdapEntry ldapEntry,
521 String name,
522 List<AttributeModification> modifications) {
523 LdapAttribute attr = ldapEntry.getAttribute(name);
524 if (attr == null) {
525 return;
526 }
527 ldapEntry.removeAttributes(attr);
528 modifications.add(
529 new AttributeModification(
530 Type.DELETE,
531 attr));
532 }
533
534
535
536
537
538
539
540
541
542
543
544
545 static <T> void removeAttribute(
546 LdapEntry ldapEntry,
547 String name,
548 T value,
549 ValueTranscoder<T> valueTranscoder,
550 List<AttributeModification> modifications) {
551 LdapAttribute attr = ldapEntry.getAttribute(name);
552 if (attr == null) {
553 return;
554 }
555 if (value == null) {
556 removeAttribute(ldapEntry, name, modifications);
557 } else {
558 removeAttributes(ldapEntry, name, Collections.singleton(value), valueTranscoder,
559 modifications);
560 }
561 }
562
563
564
565
566
567
568
569
570
571
572 static <T> void removeAttribute(
573 LdapEntry ldapEntry,
574 LdaptiveAttribute<T> attribute,
575 T value,
576 List<AttributeModification> modifications) {
577 removeAttribute(ldapEntry, attribute.getName(), value, attribute.getValueTranscoder(),
578 modifications);
579 }
580
581
582
583
584
585
586
587
588
589
590
591
592 static <T> void removeAttributes(
593 LdapEntry ldapEntry,
594 String name,
595 Collection<T> values,
596 ValueTranscoder<T> valueTranscoder,
597 List<AttributeModification> modifications) {
598
599 LdapAttribute attr = ldapEntry.getAttribute(name);
600 if (attr == null || values == null || values.isEmpty()) {
601 return;
602 }
603 List<T> newValues = new ArrayList<>(getAttributeValues(ldapEntry, name, valueTranscoder));
604 newValues.removeAll(values);
605 setAttributes(ldapEntry, name, newValues, attr.isBinary(), valueTranscoder, modifications);
606 }
607
608
609
610
611
612
613
614
615
616
617 static <T> void removeAttributes(
618 LdapEntry ldapEntry,
619 LdaptiveAttribute<T> attribute,
620 Collection<T> values,
621 List<AttributeModification> modifications) {
622 removeAttributes(ldapEntry, attribute.getName(), values, attribute.getValueTranscoder(),
623 modifications);
624 }
625
626
627
628
629
630
631
632
633
634 static String createDn(
635 String rdn,
636 String rdnValue,
637 String baseDn) {
638 return Dn.builder()
639 .add(new RDn(new NameValue(rdn, rdnValue)))
640 .add(new Dn(baseDn))
641 .build()
642 .format();
643 }
644
645
646
647
648
649
650
651 static String getRdn(String dn) {
652 if (dn == null) {
653 return null;
654 }
655 try {
656 Dn parsedDn = new Dn(dn);
657 if (parsedDn.isEmpty()) {
658 return dn;
659 }
660 return parsedDn.getRDn().getNameValue().getStringValue();
661
662 } catch (IllegalArgumentException ignored) {
663 return dn;
664 }
665 }
666
667 }