1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package net.sf.ldaptemplate.support;
18
19 import java.util.Collections;
20 import java.util.Enumeration;
21 import java.util.Iterator;
22 import java.util.LinkedList;
23 import java.util.List;
24 import java.util.ListIterator;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27
28 import javax.naming.InvalidNameException;
29 import javax.naming.Name;
30
31 import net.sf.ldaptemplate.BadLdapGrammarException;
32
33 import org.apache.commons.lang.StringUtils;
34
35 /***
36 * Default implementation of a Name corresponding to an LDAP path. A
37 * DistinguishedName implementation is included in JDK1.5 (LdapName), but not in
38 * prior releases, and this implementation is intended if a prior implementation
39 * is used.
40 *
41 * An DistinguishedName is particularly useful when building or modifying an
42 * Ldap path dynamically, as escaping will be taken care of.
43 *
44 * A path is split into several names. The Name interface specifies that the
45 * most significant part be in position 0, i.e.
46 *
47 * The path: uid=adam.skogman, ou=People, ou=EU Name[0]: ou=EU Name[1]:
48 * ou=People Name[2]: uid=adam.skogman
49 *
50 * Useful for parsing and building LDAP paths.
51 *
52 * <pre>
53 * DistinguishedName path = new DistinguishedName();
54 * path.addLast("cn", entry.getUid());
55 * path.addLast("ou", "users");
56 * path.append(new DistinguishedName(helpdesk.getSomeSuffix()));
57 * String dn = path.toString();
58 * </pre>
59 *
60 * TODO: Implement compareTo().
61 *
62 * @author Adam Skogman
63 * @author Mattias Arthursson
64 */
65 public class DistinguishedName implements Name {
66 private static final long serialVersionUID = 3514344371999042586L;
67
68 public static final DistinguishedName EMPTY_PATH = new DistinguishedName();
69
70 private LinkedList names;
71
72 protected static final Pattern NAME_PATTERN = Pattern
73 .compile("(.*?[^////])(,|;|$)");
74
75 /***
76 * Construct a new DistinguishedName with no components.
77 */
78 public DistinguishedName() {
79 names = new LinkedList();
80 }
81
82 /***
83 * Construct a new DistinguishedName from a String.
84 *
85 * @param path
86 * a String corresponding to a (syntactically) valid LDAP path.
87 */
88 public DistinguishedName(String path) {
89 parse(path);
90 }
91
92 /***
93 * Construct a new DistinguishedName from the supplied List of LdapRdn
94 * objects.
95 *
96 * @param list
97 * the components that this instance will consist of.
98 */
99 public DistinguishedName(LinkedList list) {
100 this.names = list;
101 }
102
103 /***
104 * Construct a new DistinguishedName from the supplied Name. The parts of
105 * the supplied Name must be syntactically correct LdapRdns.
106 *
107 * @param name
108 * the Name to construct a new DistinguishedName from.
109 */
110 public DistinguishedName(Name name) {
111 names = new LinkedList();
112 for (int i = 0; i < name.size(); i++) {
113 names.add(new LdapRdn(name.get(i)));
114 }
115 }
116
117 /***
118 * Parse the supplied String and make this instance represent the
119 * corresponding distinguished name.
120 *
121 * @param path
122 * the LDAP path to parse.
123 */
124 protected void parse(String path) {
125 names = new LinkedList();
126
127 if (!StringUtils.isBlank(path)) {
128
129 Matcher matcher = NAME_PATTERN.matcher(path);
130
131 while (matcher.find()) {
132 String rdnString = matcher.group(1);
133 LdapRdn name = new LdapRdn(rdnString);
134 names.add(0, name);
135 }
136 }
137 }
138
139 /***
140 * Get the LdapRdn at a specified position.
141 *
142 * @param index
143 * the LdapRdn to retrieve.
144 * @return the LdapRdn at the requested position.
145 */
146 public LdapRdn getLdapRdn(int index) {
147 return (LdapRdn) names.get(index);
148 }
149
150 /***
151 * Get the name list.
152 *
153 * @return the list of LdapRdns that this DistinguishedName consists of.
154 */
155 public LinkedList getNames() {
156 return names;
157 }
158
159 /***
160 * Get the String representation of this DistinguishedName.
161 *
162 * @return a syntactically correct, escaped String representation of the
163 * DistinguishedName.
164 */
165 public String toString() {
166 return encode();
167 }
168
169 /***
170 * Builds a complete LDAP path, ldap encoded, useful as a DN
171 *
172 * Always uses lowercase, always separates with ", " i.e. comma and a space.
173 *
174 * @return the LDAP path.
175 */
176 public String encode() {
177
178
179 if (names.size() == 0)
180 return "";
181
182 StringBuffer buffer = new StringBuffer(256);
183
184 ListIterator i = names.listIterator(names.size());
185 while (i.hasPrevious()) {
186 LdapRdn rdn = (LdapRdn) i.previous();
187 buffer.append(rdn.getLdapEncoded());
188
189
190 if (i.hasPrevious())
191 buffer.append(", ");
192 }
193
194 return buffer.toString();
195
196 }
197
198 /***
199 * Builds a complete LDAP path, ldap and url encoded. Separates only with
200 * ",".
201 *
202 * @return the LDAP path, for use in an url.
203 */
204 public String toUrl() {
205 StringBuffer buffer = new StringBuffer(256);
206
207 for (int i = names.size() - 1; i >= 0; i--) {
208 LdapRdn n = (LdapRdn) names.get(i);
209 buffer.append(n.encodeUrl());
210 if (i > 0) {
211 buffer.append(",");
212 }
213 }
214 return buffer.toString();
215 }
216
217 /***
218 * Determines if a ldap path contains another path.
219 *
220 * @param path
221 * the path to check.
222 * @return true if the supplied path is conained in this instance, false
223 * otherwise.
224 */
225 public boolean contains(DistinguishedName path) {
226
227 List shortlist = path.getNames();
228
229
230 if (getNames().size() < shortlist.size())
231 return false;
232
233
234 if (shortlist.size() == 0)
235 return false;
236
237 Iterator longiter = getNames().iterator();
238 Iterator shortiter = shortlist.iterator();
239
240 LdapRdn longname = (LdapRdn) longiter.next();
241 LdapRdn shortname = (LdapRdn) shortiter.next();
242
243
244 while (!longname.equals(shortname) && longiter.hasNext()) {
245 longname = (LdapRdn) longiter.next();
246 }
247
248
249 if (!shortiter.hasNext() && longname.equals(shortname))
250 return true;
251 if (!longiter.hasNext())
252 return false;
253
254
255 while (longname.equals(shortname) && longiter.hasNext()
256 && shortiter.hasNext()) {
257 longname = (LdapRdn) longiter.next();
258 shortname = (LdapRdn) shortiter.next();
259 }
260
261
262 if (!shortiter.hasNext() && longname.equals(shortname))
263 return true;
264 else
265 return false;
266
267 }
268
269 /***
270 * Add a LDAP path first
271 *
272 * @param path
273 */
274 public void append(DistinguishedName path) {
275 getNames().addAll(path.getNames());
276 }
277
278 /***
279 * Add a LDAP path first
280 *
281 * @param path
282 */
283 public void prepend(DistinguishedName path) {
284 ListIterator i = path.getNames().listIterator(path.getNames().size());
285 while (i.hasPrevious()) {
286 getNames().addFirst(i.previous());
287 }
288 }
289
290 /***
291 * Remove the first part of this DistinguishedName.
292 *
293 * @return the removed entry.
294 */
295 public LdapRdn removeFirst() {
296 return (LdapRdn) getNames().removeFirst();
297 }
298
299 /***
300 * Remove the supplied path from the beginning of this DistinguishedName if
301 * this instance starts with <path>. Useful for stripping base path suffix
302 * from a DistinguishedName.
303 *
304 * @param path
305 * the path to remove from the beginning of this instance.
306 */
307 public void removeFirst(Name path) {
308 if (path != null && this.startsWith(path)) {
309 for (int i = 0; i < path.size(); i++)
310 this.removeFirst();
311 }
312 }
313
314 /***
315 * @see java.lang.Object#clone()
316 */
317 public Object clone() {
318
319
320 LinkedList list = new LinkedList(getNames());
321
322 return new DistinguishedName(list);
323 }
324
325 /***
326 * @see java.lang.Object#equals(java.lang.Object)
327 */
328 public boolean equals(Object obj) {
329
330
331 if (obj == null || obj.getClass() != this.getClass()) {
332 return false;
333 }
334
335 DistinguishedName name = (DistinguishedName) obj;
336
337
338 return getNames().equals(name.getNames());
339 }
340
341 /***
342 * @see java.lang.Object#hashCode()
343 */
344 public int hashCode() {
345 return this.getClass().hashCode() ^ getNames().hashCode();
346 }
347
348 public int compareTo(Object o) {
349
350 return 0;
351 }
352
353 public int size() {
354 return names.size();
355 }
356
357 public boolean isEmpty() {
358 return names.size() == 0;
359 }
360
361
362
363
364
365
366 public Enumeration getAll() {
367 LinkedList strings = new LinkedList();
368 for (Iterator iter = names.iterator(); iter.hasNext();) {
369 LdapRdn rdn = (LdapRdn) iter.next();
370 strings.add(rdn.getLdapEncoded());
371 }
372
373 return Collections.enumeration(strings);
374 }
375
376
377
378
379
380
381 public String get(int index) {
382 LdapRdn rdn = (LdapRdn) names.get(index);
383 return rdn.getLdapEncoded();
384 }
385
386
387
388
389
390
391 public Name getPrefix(int index) {
392 LinkedList newNames = new LinkedList();
393 for (int i = 0; i < index; i++) {
394 newNames.add(names.get(i));
395 }
396
397 return new DistinguishedName(newNames);
398 }
399
400
401
402
403
404
405 public Name getSuffix(int index) {
406 if (index > names.size()) {
407 throw new ArrayIndexOutOfBoundsException();
408 }
409
410 LinkedList newNames = new LinkedList();
411 for (int i = index; i < names.size(); i++) {
412 newNames.add(names.get(i));
413 }
414
415 return new DistinguishedName(newNames);
416 }
417
418
419
420
421
422
423 public boolean startsWith(Name name) {
424 if (name.size() == 0) {
425 return false;
426 }
427
428 DistinguishedName start = null;
429 if (name instanceof DistinguishedName) {
430 start = (DistinguishedName) name;
431 } else {
432 return false;
433 }
434
435 if (start.size() > this.size()) {
436 return false;
437 }
438
439 Iterator longiter = names.iterator();
440 Iterator shortiter = start.getNames().iterator();
441
442 while (shortiter.hasNext()) {
443 Object longname = longiter.next();
444 Object shortname = shortiter.next();
445
446 if (!longname.equals(shortname)) {
447 return false;
448 }
449 }
450
451
452 return true;
453 }
454
455 /***
456 * Determines if this ldap path ends with a certian path.
457 *
458 * If the argument path is empty (no names in path) this methid will return
459 * false.
460 *
461 * @param name
462 * The suffix to check for
463 *
464 */
465 public boolean endsWith(Name name) {
466 DistinguishedName path = null;
467 if (name instanceof DistinguishedName) {
468 path = (DistinguishedName) name;
469 } else {
470 return false;
471 }
472
473 List shortlist = path.getNames();
474
475
476 if (getNames().size() < shortlist.size())
477 return false;
478
479
480 if (shortlist.size() == 0)
481 return false;
482
483 ListIterator longiter = getNames().listIterator(getNames().size());
484 ListIterator shortiter = shortlist.listIterator(shortlist.size());
485
486 while (shortiter.hasPrevious()) {
487 LdapRdn longname = (LdapRdn) longiter.previous();
488 LdapRdn shortname = (LdapRdn) shortiter.previous();
489
490 if (!longname.equals(shortname))
491 return false;
492 }
493
494
495 return true;
496
497 }
498
499
500
501
502
503
504 public Name addAll(Name name) throws InvalidNameException {
505 return addAll(names.size(), name);
506 }
507
508
509
510
511
512
513 public Name addAll(int arg0, Name name) throws InvalidNameException {
514 DistinguishedName distinguishedName = null;
515 try {
516 distinguishedName = (DistinguishedName) name;
517 } catch (ClassCastException e) {
518 throw new InvalidNameException("Invalid name type");
519 }
520
521 names.addAll(arg0, distinguishedName.getNames());
522 return this;
523 }
524
525
526
527
528
529
530 public Name add(String string) throws InvalidNameException {
531 return add(names.size(), string);
532 }
533
534
535
536
537
538
539 public Name add(int index, String string) throws InvalidNameException {
540 try {
541 names.add(index, new LdapRdn(string));
542 } catch (BadLdapGrammarException e) {
543 throw new InvalidNameException("Failed to parse rdn '" + string
544 + "'");
545 }
546 return this;
547 }
548
549
550
551
552
553
554 public Object remove(int arg0) throws InvalidNameException {
555 LdapRdn rdn = (LdapRdn) names.remove(arg0);
556 return rdn.getLdapEncoded();
557 }
558
559 /***
560 * Remove the ldast part of this DistinguishedName.
561 *
562 * @return the removed LdapRdn.
563 */
564 public LdapRdn removeLast() {
565 return (LdapRdn) getNames().removeLast();
566 }
567
568 public void add(String key, String value) {
569 names.add(new LdapRdn(key, value));
570 }
571 }