Add C# port, from Alexander Gnauck <gnauck@ag-software.de>.
[libidn.git] / csharp / Stringprep.cs
blobe1f4d5e695f00109a5c95f74d799de119ff79f6a
1 /// <summary> Copyright (C) 2004 Free Software Foundation, Inc.
2 /// *
3 /// Author: Alexander Gnauck AG-Software
4 /// *
5 /// This file is part of GNU Libidn.
6 /// *
7 /// This library is free software; you can redistribute it and/or
8 /// modify it under the terms of the GNU Lesser General Public License
9 /// as published by the Free Software Foundation; either version 2.1 of
10 /// the License, or (at your option) any later version.
11 /// *
12 /// This library is distributed in the hope that it will be useful, but
13 /// WITHOUT ANY WARRANTY; without even the implied warranty of
14 /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 /// Lesser General Public License for more details.
16 /// *
17 /// You should have received a copy of the GNU Lesser General Public
18 /// License along with this library; if not, write to the Free Software
19 /// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20 /// USA
21 /// </summary>
23 using System;
25 namespace gnu.inet.encoding
27 /// <summary> This class offers static methods for preparing internationalized
28 /// strings. It supports the following stringprep profiles:
29 /// <ul>
30 /// <li>RFC3491 nameprep
31 /// <li>RFC3920 XMPP nodeprep and resourceprep
32 /// </ul>
33 /// Note that this implementation only supports 16-bit Unicode code
34 /// points.
35 /// </summary>
36 public class Stringprep
38 /// <summary> Preps a name according to the Stringprep profile defined in
39 /// RFC3491. Unassigned code points are not allowed.
40 /// *
41 /// </summary>
42 /// <param name="input">the name to prep.
43 /// </param>
44 /// <returns> the prepped name.
45 /// @throws StringprepException If the name cannot be prepped with
46 /// this profile.
47 /// @throws NullPointerException If the name is null.
48 ///
49 /// </returns>
50 public static System.String nameprep(System.String input)
52 return nameprep(input, false);
55 /// <summary> Preps a name according to the Stringprep profile defined in
56 /// RFC3491.
57 /// *
58 /// </summary>
59 /// <param name="input">the name to prep.
60 /// </param>
61 /// <param name="allowUnassigned">true if the name may contain unassigned
62 /// code points.
63 /// </param>
64 /// <returns> the prepped name.
65 /// @throws StringprepException If the name cannot be prepped with
66 /// this profile.
67 /// @throws NullPointerException If the name is null.
68 ///
69 /// </returns>
70 public static System.String nameprep(System.String input, bool allowUnassigned)
72 if (input == null)
74 throw new System.NullReferenceException();
77 System.Text.StringBuilder s = new System.Text.StringBuilder(input);
79 if (!allowUnassigned && contains(s, RFC3454.A1))
81 throw new StringprepException(StringprepException.CONTAINS_UNASSIGNED);
84 filter(s, RFC3454.B1);
85 map(s, RFC3454.B2search, RFC3454.B2replace);
87 s = new System.Text.StringBuilder(NFKC.normalizeNFKC(s.ToString()));
88 // B.3 is only needed if NFKC is not used, right?
89 // map(s, RFC3454.B3search, RFC3454.B3replace);
91 if (contains(s, RFC3454.C12) || contains(s, RFC3454.C22) || contains(s, RFC3454.C3) || contains(s, RFC3454.C4) || contains(s, RFC3454.C5) || contains(s, RFC3454.C6) || contains(s, RFC3454.C7) || contains(s, RFC3454.C8))
93 // Table C.9 only contains code points > 0xFFFF which Java
94 // doesn't handle
95 throw new StringprepException(StringprepException.CONTAINS_PROHIBITED);
98 // Bidi handling
99 bool r = contains(s, RFC3454.D1);
100 bool l = contains(s, RFC3454.D2);
102 // RFC 3454, section 6, requirement 1: already handled above (table C.8)
104 // RFC 3454, section 6, requirement 2
105 if (r && l)
107 throw new StringprepException(StringprepException.BIDI_BOTHRAL);
110 // RFC 3454, section 6, requirement 3
111 if (r)
113 if (!contains(s[0], RFC3454.D1) || !contains(s[s.Length - 1], RFC3454.D1))
115 throw new StringprepException(StringprepException.BIDI_LTRAL);
119 return s.ToString();
123 * Characters prohibited by RFC3920 nodeprep that aren't defined as
124 * part of the RFC3454 tables.
126 private static char [] RFC3920_NODEPREP_PROHIBIT = new char [] {
127 '\u0022', '\u0026', '\'', '\u002F',
128 '\u003A', '\u003C', '\u003E', '\u0040'
131 /// <summary> Preps a node name according to the Stringprep profile defined in
132 /// RFC3920. Unassigned code points are not allowed.
133 /// *
134 /// </summary>
135 /// <param name="input">the node name to prep.
136 /// </param>
137 /// <returns> the prepped node name.
138 /// @throws StringprepException If the node name cannot be prepped
139 /// with this profile.
140 /// @throws NullPointerException If the node name is null.
141 ///
142 /// </returns>
143 public static System.String nodeprep(System.String input)
145 return nodeprep(input, false);
148 /// <summary> Preps a node name according to the Stringprep profile defined in
149 /// RFC3920.
150 /// *
151 /// </summary>
152 /// <param name="input">the node name to prep.
153 /// </param>
154 /// <param name="allowUnassigned">true if the node name may contain
155 /// unassigned code points.
156 /// </param>
157 /// <returns> the prepped node name.
158 /// @throws StringprepException If the node name cannot be prepped
159 /// with this profile.
160 /// @throws NullPointerException If the node name is null.
161 ///
162 /// </returns>
163 public static System.String nodeprep(System.String input, bool allowUnassigned)
165 if (input == null)
167 throw new System.NullReferenceException();
170 System.Text.StringBuilder s = new System.Text.StringBuilder(input);
172 if (!allowUnassigned && contains(s, RFC3454.A1))
174 throw new StringprepException(StringprepException.CONTAINS_UNASSIGNED);
177 filter(s, RFC3454.B1);
178 map(s, RFC3454.B2search, RFC3454.B2replace);
180 s = new System.Text.StringBuilder(NFKC.normalizeNFKC(s.ToString()));
182 if (contains(s, RFC3454.C11) || contains(s, RFC3454.C12) || contains(s, RFC3454.C21) || contains(s, RFC3454.C22) || contains(s, RFC3454.C3) || contains(s, RFC3454.C4) || contains(s, RFC3454.C5) || contains(s, RFC3454.C6) || contains(s, RFC3454.C7) || contains(s, RFC3454.C8) || contains(s, RFC3920_NODEPREP_PROHIBIT))
184 // Table C.9 only contains code points > 0xFFFF which Java
185 // doesn't handle
186 throw new StringprepException(StringprepException.CONTAINS_PROHIBITED);
189 // Bidi handling
190 bool r = contains(s, RFC3454.D1);
191 bool l = contains(s, RFC3454.D2);
193 // RFC 3454, section 6, requirement 1: already handled above (table C.8)
195 // RFC 3454, section 6, requirement 2
196 if (r && l)
198 throw new StringprepException(StringprepException.BIDI_BOTHRAL);
201 // RFC 3454, section 6, requirement 3
202 if (r)
204 if (!contains(s[0], RFC3454.D1) || !contains(s[s.Length - 1], RFC3454.D1))
206 throw new StringprepException(StringprepException.BIDI_LTRAL);
210 return s.ToString();
213 /// <summary> Preps a resource name according to the Stringprep profile defined
214 /// in RFC3920. Unassigned code points are not allowed.
215 /// *
216 /// </summary>
217 /// <param name="input">the resource name to prep.
218 /// </param>
219 /// <returns> the prepped node name.
220 /// @throws StringprepException If the resource name cannot be prepped
221 /// with this profile.
222 /// @throws NullPointerException If the resource name is null.
223 ///
224 /// </returns>
225 public static System.String resourceprep(System.String input)
227 return resourceprep(input, false);
230 /// <summary> Preps a resource name according to the Stringprep profile defined
231 /// in RFC3920.
232 /// *
233 /// </summary>
234 /// <param name="input">the resource name to prep.
235 /// </param>
236 /// <param name="allowUnassigned">true if the resource name may contain
237 /// unassigned code points.
238 /// </param>
239 /// <returns> the prepped node name.
240 /// @throws StringprepException If the resource name cannot be prepped
241 /// with this profile.
242 /// @throws NullPointerException If the resource name is null.
243 ///
244 /// </returns>
245 public static System.String resourceprep(System.String input, bool allowUnassigned)
247 if (input == null)
249 throw new System.NullReferenceException();
252 System.Text.StringBuilder s = new System.Text.StringBuilder(input);
254 if (!allowUnassigned && contains(s, RFC3454.A1))
256 throw new StringprepException(StringprepException.CONTAINS_UNASSIGNED);
259 filter(s, RFC3454.B1);
261 s = new System.Text.StringBuilder(NFKC.normalizeNFKC(s.ToString()));
263 if (contains(s, RFC3454.C12) || contains(s, RFC3454.C21) || contains(s, RFC3454.C22) || contains(s, RFC3454.C3) || contains(s, RFC3454.C4) || contains(s, RFC3454.C5) || contains(s, RFC3454.C6) || contains(s, RFC3454.C7) || contains(s, RFC3454.C8))
265 // Table C.9 only contains code points > 0xFFFF which Java
266 // doesn't handle
267 throw new StringprepException(StringprepException.CONTAINS_PROHIBITED);
270 // Bidi handling
271 bool r = contains(s, RFC3454.D1);
272 bool l = contains(s, RFC3454.D2);
274 // RFC 3454, section 6, requirement 1: already handled above (table C.8)
276 // RFC 3454, section 6, requirement 2
277 if (r && l)
279 throw new StringprepException(StringprepException.BIDI_BOTHRAL);
282 // RFC 3454, section 6, requirement 3
283 if (r)
285 if (!contains(s[0], RFC3454.D1) || !contains(s[s.Length - 1], RFC3454.D1))
287 throw new StringprepException(StringprepException.BIDI_LTRAL);
291 return s.ToString();
294 internal static bool contains(System.Text.StringBuilder s, char[] p)
296 for (int i = 0; i < p.Length; i++)
298 char c = p[i];
299 for (int j = 0; j < s.Length; j++)
301 if (c == s[j])
303 return true;
307 return false;
310 internal static bool contains(System.Text.StringBuilder s, char[][] p)
312 for (int i = 0; i < p.Length; i++)
314 char[] r = p[i];
315 if (1 == r.Length)
317 char c = r[0];
318 for (int j = 0; j < s.Length; j++)
320 if (c == s[j])
322 return true;
326 else if (2 == r.Length)
328 char f = r[0];
329 char t = r[1];
330 for (int j = 0; j < s.Length; j++)
332 if (f <= s[j] && t >= s[j])
334 return true;
339 return false;
342 internal static bool contains(char c, char[][] p)
344 for (int i = 0; i < p.Length; i++)
346 char[] r = p[i];
347 if (1 == r.Length)
349 if (c == r[0])
351 return true;
354 else if (2 == r.Length)
356 char f = r[0];
357 char t = r[1];
358 if (f <= c && t >= c)
360 return true;
364 return false;
367 internal static void filter(System.Text.StringBuilder s, char[] f)
369 for (int i = 0; i < f.Length; i++)
371 char c = f[i];
373 int j = 0;
374 while (j < s.Length)
376 if (c == s[j])
378 //s.deleteCharAt(j);
379 s.Remove(j, 1);
381 else
383 j++;
389 internal static void filter(System.Text.StringBuilder s, char[][] f)
391 for (int i = 0; i < f.Length; i++)
393 char[] r = f[i];
395 if (1 == r.Length)
397 char c = r[0];
399 int j = 0;
400 while (j < s.Length)
402 if (c == s[j])
404 //s.deleteCharAt(j);
405 s.Remove(j, 1);
407 else
409 j++;
413 else if (2 == r.Length)
415 char from = r[0];
416 char to = r[1];
418 int j = 0;
419 while (j < s.Length)
421 if (from <= s[j] && to >= s[j])
423 //s.deleteCharAt(j);
424 s.Remove(j, 1);
426 else
428 j++;
435 internal static void map(System.Text.StringBuilder s, char[] search, System.String[] replace)
437 for (int i = 0; i < search.Length; i++)
439 char c = search[i];
441 int j = 0;
442 while (j < s.Length)
444 if (c == s[j])
446 //s.deleteCharAt(j);
447 s.Remove(j, 1);
448 if (null != replace[i])
450 s.Insert(j, replace[i]);
451 j += replace[i].Length - 1;
454 else
456 j++;