KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ca > commons > naming > NameUtility


1 package com.ca.commons.naming;
2
3 import com.ca.commons.cbutil.CBParse;
4 import javax.naming.InvalidNameException JavaDoc;
5
6 /**
7  * Some specialised String utilities for parsing ldap names.
8  *
9  */

10  
11 public class NameUtility
12 {
13     /** return position of next non-escaped character 'c' in a DN.
14      * (i.e. character 'c' without a preceeding '\' character, or
15      * not in double quotes) (unless the char is a quote, in which
16      * case it isn't. um.). Note that by definition '\' cannot exist,
17      * unescaped, as a first class character and therefore this function
18      * will not find '\'s.
19      */

20
21     public static int next(String JavaDoc searchMe, int startpos, char c)
22     {
23         if (c=='\\') return -1; // can't have an unescaped slash.
24

25         if (startpos < 0 || startpos > searchMe.length()) return -1; // can't search outside string.
26

27         int escape=-1, quotes=-1, nextC=-1;
28         
29         while (true)
30         {
31             if (escape<startpos) escape = searchMe.indexOf('\\', startpos);
32             if (quotes<startpos) quotes = searchMe.indexOf('"', startpos);
33             if (nextC<startpos) nextC = searchMe.indexOf(c, startpos);
34
35             // check for trivial case - no escaped characters...
36
if (escape==-1 && quotes==-1)
37                 return nextC;
38     
39             // second trivial case - char occurs before any possible escaping
40
// NOTE - if the search char is a quote, this will return the first quote
41
// (rather than treat it as an escape character).
42
if ( ((escape == -1) || (nextC < escape)) && ((quotes == -1) || (nextC <= quotes)) )
43                 return nextC;
44     
45             // if a slash escape is the next thing, then move past it...
46

47             if (quotes == -1 || (escape != -1 && escape<quotes))
48             {
49                 startpos = escape+2; // flip past the next escape
50
}
51             else // handle quoted text (above code implicitly assures us that the leading quote is unescaped)
52
{
53                 boolean escaped = true; // flag indicating that a particular quote character is 'escaped' (i.e. \")
54

55                 while (escaped)
56                 {
57                     quotes = searchMe.indexOf('"', quotes+1); // find next quote
58
if (quotes == -1) return -1; // ERROR;
59

60                     int backcheck = quotes-1; // make sure we only
61
while (searchMe.charAt(backcheck--) == '\\') // count unescaped quotes
62
escaped = !escaped;
63                         
64                     escaped = !escaped;
65                 }
66                 
67                 startpos = quotes+1;
68             }
69         }
70     }
71
72
73     /**
74      * A commutative twin to escape(), this removes the leading
75      * slashes from a string.
76      */

77     
78     public static String JavaDoc unescape(String JavaDoc string)
79         throws InvalidNameException JavaDoc
80     {
81         return unescape(string, false);
82     }
83
84     /*
85      * takes a string that is (possible) in ldap dn escaped utf8 format.
86      * (e.g. something like cn=\E5\B0\8F\E7\AC\A0 etc.)
87      */

88      
89     public static String JavaDoc removeEscapedUTF(String JavaDoc utfString)
90         throws InvalidNameException JavaDoc
91     {
92         try
93         {
94             boolean foundUTF = false;
95             int safeLen = utfString.length()-1;
96             int pos = utfString.indexOf('\\');
97             
98             while ( pos > -1 && pos < safeLen)
99             {
100                 char c = utfString.charAt(pos+1);
101                 if ("01234567890ABCDEFabcdef".indexOf(c) > 0)
102                 {
103                     foundUTF = true;
104                     char c2 = utfString.charAt(pos+2);
105                     if ("01234567890ABCDEFabcdef".indexOf(c2) == -1)
106                         throw new InvalidNameException JavaDoc("second char of escaped hex couplet wasn't hex; was '" + c2 + "'");
107                         
108                     char utf8 = (char)Integer.parseInt("" + c + c2, 16);
109                     utfString = utfString.substring(0,pos) + utf8 + utfString.substring(pos+3);
110                     pos = utfString.indexOf('\\',pos+1);
111                 }
112                 else
113                 {
114                     pos = utfString.indexOf('\\',pos+1); // skip normally escaped ldap character (e.g. \+ or \= )
115
}
116             }
117             
118             if (foundUTF) // read the string as ascii (8859-1) bytes, and then interpret
119
{ // those bytes as utf8 to make a java unicode internal string...
120
utfString = new String JavaDoc(utfString.getBytes("ISO-8859-1"), "UTF8");
121             }
122         }
123         catch (Exception JavaDoc e)
124         {
125             e.printStackTrace();
126             throw new InvalidNameException JavaDoc("unable to parse rdn val: '" + utfString + "' - raw error was: " + e.toString());
127         }
128         
129         return utfString;
130     }
131
132
133     /**
134      * Removes the escaping. If jndiHack is false, this is roughly the
135      * opposite of escape (however multiple valid formats resolve to the
136      * same final unicode string).
137      * If jndiHack is true, it does special handling to cope with both
138      * version 2 ldap escapes, and the wonky return values for slashes
139      * given by jndi.<p>
140      *
141      * If used for DNs, The string argument should be the smallest
142      * possible unit of an rdn value.<p>
143      *
144      * If the argument has beginning and end quotes, they are removed, leaving
145      * text otherwise untouched (ldap v2 escaping).<br>
146      *
147      * Otherwise, remove all leading '\' characters, giving special handling to
148      * escaped slashes.
149      * <p>
150      * <i>Note that in the second form this ftn is <b>not</b> commutative with escape</i>.
151      *
152      * @param string the string to remove escape characters from
153      * @param jndiHack if true, indicates special handling for ldap v2 escaping, and
154      * wierd jndi return values.
155      */

156
157      
158     public static String JavaDoc unescape(String JavaDoc string, boolean jndiHack)
159         throws InvalidNameException JavaDoc
160     {
161         int len = string.length();
162         if (len == 0) return string;
163     
164         if (string.charAt(0)=='\"')
165         {
166             if (string.charAt(string.length()-1)!='\"') // whole string *must* be quoted
167
throw new InvalidNameException JavaDoc("RDN.unescape(): invalid rdn fragment '" + ((string==null)?"<null>":string) + "'");
168                 
169             string = string.substring(1,string.length()-1);
170         }
171         else
172         {
173             string = handleEscapedCharacters(string);
174         }
175         return string;
176     }
177
178
179     /**
180      * handle ldap escaped characters as per rfc 2253
181      * In short - ',', '+', '=', '<', '>', '#', ';', '"' are escaped with
182      * a backslash, and utf8 can be escaped as a hexpair backslash
183      *
184      */

185      
186     private static String JavaDoc handleEscapedCharacters(String JavaDoc string)
187         throws InvalidNameException JavaDoc
188     {
189         if (string.indexOf('\\') == -1)
190             return string;
191
192         boolean hasUTF8 = false; // whether a utf8 string has been found...
193
int pos; // position of most recently found slash
194

195         StringBuffer JavaDoc buffy = new StringBuffer JavaDoc(string);
196             
197         try
198         {
199             pos = string.indexOf("\\");
200             while ( pos > -1)
201             {
202                 if (pos == buffy.length()-1) // XXX trailing escaped ' ' bug!
203
{
204                     buffy.setCharAt(pos, ' '); //put the space back
205
}
206                 else
207                 {
208                     
209                     char c = buffy.charAt(pos+1);
210
211                     if (("\",=+<>#;\\ ".indexOf(c)) >= 0)
212                     {
213                         buffy.deleteCharAt(pos); // remove leading slashes
214
}
215                     else if (("0123456789abcdefABCDEF").indexOf(c) >= 0)
216                     {
217                         hasUTF8 = true; // we'll cope with this seperately below...
218
pos += 2; // skip past the two hex digits and keep going...
219
}
220                     else
221                     {
222                         throw new InvalidNameException JavaDoc("illegal escaped character '" + c + "' in name: '" + string + "' (NameUtility:handleEscapedCharacters() ).");
223                     }
224                 }
225                 pos = buffy.toString().indexOf("\\",pos+1); // try to find next slash to work on.
226
}
227         }
228         catch (StringIndexOutOfBoundsException JavaDoc e)
229         {
230             throw new InvalidNameException JavaDoc("unparsable string '" + string + "' in NameUtility");
231         }
232             
233         if (hasUTF8)
234             return removeEscapedUTF(buffy.toString());
235         else
236             return buffy.toString();
237     }
238
239
240     
241     // there *SHOULD* only be two quotes: at the beginning and the end. Trim 'em off
242
// and hope (only here for backwards compatibility with RFC 1779 anyway!)
243
/**
244      * Not currently used?
245      */

246     public static String JavaDoc trimQuotes(String JavaDoc string)
247     {
248         int pos = string.indexOf('\"');
249         int pos2 = string.lastIndexOf('\"');
250         if (pos == -1 || pos == pos2)
251             System.out.println("RDN.trimQuotes(): rare error parsing rdn fragment: " + string); // return string unchanged.
252
else
253             string = string.substring(0,pos) + string.substring(pos+1,pos2) + string.substring(pos2+1);
254         return string;
255     }
256
257     /* (Obsolete - kept as backup. Now using new CompositeName(..) followed
258     * by get(0), which returns something pretty close to ldap encoding).
259      *
260      * Extra special handling for amazing jndi behaviour.
261      * The rule is - each slash was translated to three slashes,
262      * Unless behind a special character (including a slash), in
263      * which case it is worth four... This funtion translates them
264      * *back* to correct ldap format (one original slash = two slashes)
265      * So that the simple parser in unescape will work on them correctly.<p>
266      *
267      * If jndi ever started working correctly, this will become redundant.
268      */

269     protected static String JavaDoc cleanupSlashes(String JavaDoc string)
270     {
271         int pos = 0;
272         int next3group;
273         int next4group;
274         while (pos > -1)
275         {
276             next4group = string.indexOf("\\\\\\\\",pos);
277             next3group = string.indexOf("\\\\\\",pos);
278             
279             if (next3group == -1) //end of loop (3 group is subset of 4 group, so no more groups!)
280
{
281                 pos = -1;
282             }
283             else if ((next4group==-1) || (next3group<next4group)) // replace 3 group with a double slash (removed be standard parser later)
284
{
285                 string = string.substring(0,next3group) + string.substring(next3group+1);
286                 pos = next3group + 2;
287             }
288             else // translate the next four group.
289
{
290                 string = string.substring(0,next4group) + string.substring(next4group+2);
291                 pos = next4group + 2;
292             }
293         }
294         return string;
295     }
296     
297     /**
298      * escapes special characters using a backslash, as per RFC 2253.
299      * IN ADDITION: escapes forward slash '/' characters for jndi
300      * @param string the string to convert to escaped form.
301      */

302      
303     public static String JavaDoc escape(String JavaDoc string)
304     {
305         if (string == null || string.length() == 0)
306                 return string;
307                 
308         StringBuffer JavaDoc buffy = new StringBuffer JavaDoc(string);
309
310         buffy = CBParse.replaceAllBufferChar(buffy, '\\',"\\\\");
311         buffy = CBParse.replaceAllBufferChar(buffy, ',',"\\,");
312         buffy = CBParse.replaceAllBufferChar(buffy, '=',"\\=");
313         buffy = CBParse.replaceAllBufferChar(buffy, '+',"\\+");
314         buffy = CBParse.replaceAllBufferChar(buffy, '<',"\\<");
315         buffy = CBParse.replaceAllBufferChar(buffy, '>',"\\>");
316         buffy = CBParse.replaceAllBufferChar(buffy, '#',"\\#");
317         buffy = CBParse.replaceAllBufferChar(buffy, ';',"\\;");
318         buffy = CBParse.replaceAllBufferChar(buffy, '\"',"\\\"");
319         
320         if (buffy.charAt(buffy.length()-1) == ' ') // final space check
321
{
322             buffy.setCharAt(buffy.length()-1, '\\');
323             buffy.append(' ');
324         }
325   // buffy = CBUtility.replaceAllBufferChar(buffy, '/',"\\/");
326
string = buffy.toString();
327         
328         return buffy.toString();
329     }
330
331   
332     /**
333      * Apparently jndi does not handle end spaces correctly.
334      * This checks for the condition that the DN is illegal,
335      * with a dangling slash on the end, representing a DN
336      * with an end space mashed by jndi.
337      * @param ldapDNString the potentially illegal DN to rescue
338      * @return the corrected string (unchanged if no correcting required)
339      */

340      
341     public static String JavaDoc checkEndSpaces(String JavaDoc ldapDNString)
342     {
343         // Check how many slashes there are on the end.
344
// We are looking for an ending double slash
345
// (a normal escape at this stage would look like
346
// a double slash followed by an escape character,
347
// e.g. "\\," or "\\+". just "\\" is bad...);
348
// The fuss below is to cope with a DN that ends with
349
// escaped slashes, *followed* by a space (a pathalogical
350
// case, but we must be carefull!)
351

352         int finalPos = ldapDNString.length() - 1;
353         int pos = finalPos;
354         
355         while (ldapDNString.charAt(pos) == '\\') // remember '\\' is a *single* slash!
356
{
357             pos--;
358         }
359
360         int numSlashes = finalPos - pos;
361
362         if (numSlashes%4 == 2)
363         {
364             return ldapDNString + " ";
365         }
366             
367         return ldapDNString;
368     }
369
370
371 }
Popular Tags