View Javadoc

1   /*
2    * Copyright 2002-2005 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package net.sf.ldaptemplate.support;
18  
19  import java.net.URI;
20  import java.net.URISyntaxException;
21  import java.util.regex.Matcher;
22  import java.util.regex.Pattern;
23  
24  import net.sf.ldaptemplate.BadLdapGrammarException;
25  
26  import org.apache.commons.lang.StringUtils;
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  
30  /***
31   * Datatype for a LDAP name, a part of a path.
32   * 
33   * The name: uid=adam.skogman Key: uid Value: adam.skogman
34   * 
35   * @author Adam Skogman
36   */
37  public class LdapRdn {
38  
39      private String key;
40  
41      private String value;
42  
43      private String ldapEncoded;
44  
45      protected static final Pattern RDN_PATTERN = Pattern
46              .compile("//s*(//p{Alnum}+)//s*=//s*((//////p{XDigit}{2}|////.|[^////])+?)//s*");
47  
48      private static final Log log = LogFactory.getLog(LdapRdn.class);
49      
50      /***
51       * Constructs a RDN from an ldap encoded rdn "foo=bar".
52       * 
53       * The key will be forced to lowercase. The value is kept as is.
54       * 
55       * The ldapEncoded value will not be the same, but rather a reencoded value,
56       * so that all looks the same.
57       * 
58       * @param ldapRdn
59       * @throws BadLdapGrammarException
60       *             If the rdn could not be parsed.
61       */
62      public LdapRdn(String ldapRdn) throws BadLdapGrammarException {
63  
64          parseLdap(ldapRdn);
65  
66      }
67  
68      /***
69       * Constructs a RDN from a key and a value.
70       * 
71       * The key will be forced to lowercase. The value is kept as is.
72       * 
73       * @param key
74       *            Not blank
75       * @param value
76       *            Not blank
77       */
78      public LdapRdn(String key, String value) {
79  
80          if (StringUtils.isBlank(key))
81              throw new IllegalArgumentException("Key may not be blank");
82  
83          if (StringUtils.isBlank(value))
84              throw new IllegalArgumentException("Value may not be blank");
85  
86          this.key = key.toLowerCase();
87          this.value = value;
88  
89          this.ldapEncoded = encodeLdap();
90  
91      }
92  
93      /***
94       * Encode key and value to ldap
95       * 
96       * @return The ldap encoded rdn
97       */
98      protected String encodeLdap() {
99  
100         StringBuffer buff = new StringBuffer(key.length() + value.length() * 2);
101 
102         buff.append(key);
103         buff.append('=');
104         buff.append(LdapEncoder.nameEncode(value));
105 
106         return buff.toString();
107     }
108 
109     /***
110      * @param rdn
111      *            The ldap rdn to parse
112      */
113     protected void parseLdap(String rdn) throws BadLdapGrammarException {
114 
115         Matcher matcher = RDN_PATTERN.matcher(rdn);
116 
117         if (!matcher.matches()) {
118             throw new BadLdapGrammarException(
119                     "Not a proper name (such as key=value): " + rdn);
120         }
121 
122         String rawKey = matcher.group(1);
123         String rawValue = matcher.group(2);
124 
125         this.key = StringUtils.lowerCase(rawKey);
126         this.value = LdapEncoder.nameDecode(rawValue);
127 
128         if (key == null) {
129             throw new BadLdapGrammarException("Key empty in rdn '" + rdn + "'");
130         }
131         if (value == null) {
132             throw new BadLdapGrammarException("Value empty in rdn '" + rdn
133                     + "'");
134         }
135 
136         this.ldapEncoded = encodeLdap();
137 
138     }
139 
140     /***
141      * Parse the supplied String for a valid LdapRdn. Swallow any exceptions.
142      * 
143      * @param rdn
144      *            the String to parse.
145      * @return an LdapRdn corresponding to the supplied String if the format was
146      *         valid, null otherwise.
147      */
148     public static LdapRdn parse(String rdn) {
149 
150         try {
151             return new LdapRdn(rdn);
152         } catch (BadLdapGrammarException e) {
153             return null;
154         }
155 
156     }
157 
158     public String getKey() {
159         return key;
160     }
161 
162     public String getValue() {
163         return value;
164     }
165 
166     /***
167      * 
168      * 
169      * @see java.lang.Object#toString()
170      */
171     public String toString() {
172         return ldapEncoded;
173     }
174 
175     /***
176      * @see java.lang.Object#equals(java.lang.Object)
177      */
178     public boolean equals(Object obj) {
179         // A subclass with identical values should NOT be considered equal.
180         // EqualsBuilder in commons-lang cannot handle subclasses correctly.
181         if (obj == null || obj.getClass() != this.getClass()) {
182             return false;
183         }
184 
185         LdapRdn rdn = (LdapRdn) obj;
186 
187         return StringUtils.equalsIgnoreCase(rdn.key, this.key)
188                 && StringUtils.equalsIgnoreCase(rdn.value, this.value);
189     }
190 
191     public Object encodeUrl() {
192         // Use the URI class to properly URL encode the value.
193         try {
194             URI valueUri = new URI(null, null, value, null);
195             return key + "=" + valueUri.toString();
196         } catch (URISyntaxException e) {
197             // This should really never happen...
198             log.error("Failed to URL encode value " + value, e);
199             return key + "=" + "value";
200         }
201     }
202 
203     /***
204      * @see java.lang.Object#hashCode()
205      */
206     public int hashCode() {
207         return key.hashCode() ^ value.hashCode();
208     }
209 
210     /***
211      * @return The LdapRdn as a string where the value is LDAP-encoded.
212      */
213     public String getLdapEncoded() {
214         return ldapEncoded;
215     }
216 }