Bump versions.
[libidn.git] / csharp / Stringprep.cs
blob392ac050287d0b56ee0ab97945d3b491c5f5395b
1 /// <summary>
2 /// Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
3 /// *
4 /// Author: Alexander Gnauck AG-Software
5 /// *
6 /// This file is part of GNU Libidn.
7 /// *
8 /// This program is free software; you can redistribute it and/or
9 /// modify it under the terms of the GNU General Public License as
10 /// published by the Free Software Foundation; either version 2 of the
11 /// License, or (at your option) any later version.
12 /// *
13 /// This program is distributed in the hope that it will be useful,
14 /// but WITHOUT ANY WARRANTY; without even the implied warranty of
15 /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 /// General Public License for more details.
17 /// *
18 /// You should have received a copy of the GNU General Public License
19 /// along with this program; if not, write to the Free Software
20 /// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 /// 02111-1307 USA.
22 /// </summary>
24 using System;
26 namespace gnu.inet.encoding
28 /// <summary> This class offers static methods for preparing internationalized
29 /// strings. It supports the following stringprep profiles:
30 /// <ul>
31 /// <li>RFC3491 nameprep
32 /// <li>RFC3920 XMPP nodeprep and resourceprep
33 /// </ul>
34 /// Note that this implementation only supports 16-bit Unicode code
35 /// points.
36 /// </summary>
37 public class Stringprep
39 /// <summary> Preps a name according to the Stringprep profile defined in
40 /// RFC3491. Unassigned code points are not allowed.
41 /// *
42 /// </summary>
43 /// <param name="input">the name to prep.
44 /// </param>
45 /// <returns> the prepped name.
46 /// @throws StringprepException If the name cannot be prepped with
47 /// this profile.
48 /// @throws NullPointerException If the name is null.
49 ///
50 /// </returns>
51 public static System.String nameprep(System.String input)
53 return nameprep(input, false);
56 /// <summary> Preps a name according to the Stringprep profile defined in
57 /// RFC3491.
58 /// *
59 /// </summary>
60 /// <param name="input">the name to prep.
61 /// </param>
62 /// <param name="allowUnassigned">true if the name may contain unassigned
63 /// code points.
64 /// </param>
65 /// <returns> the prepped name.
66 /// @throws StringprepException If the name cannot be prepped with
67 /// this profile.
68 /// @throws NullPointerException If the name is null.
69 ///
70 /// </returns>
71 public static System.String nameprep(System.String input, bool allowUnassigned)
73 if (input == null)
75 throw new System.NullReferenceException();
78 System.Text.StringBuilder s = new System.Text.StringBuilder(input);
80 if (!allowUnassigned && contains(s, RFC3454.A1))
82 throw new StringprepException(StringprepException.CONTAINS_UNASSIGNED);
85 filter(s, RFC3454.B1);
86 map(s, RFC3454.B2search, RFC3454.B2replace);
88 s = new System.Text.StringBuilder(NFKC.normalizeNFKC(s.ToString()));
89 // B.3 is only needed if NFKC is not used, right?
90 // map(s, RFC3454.B3search, RFC3454.B3replace);
92 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))
94 // Table C.9 only contains code points > 0xFFFF which Java
95 // doesn't handle
96 throw new StringprepException(StringprepException.CONTAINS_PROHIBITED);
99 // Bidi handling
100 bool r = contains(s, RFC3454.D1);
101 bool l = contains(s, RFC3454.D2);
103 // RFC 3454, section 6, requirement 1: already handled above (table C.8)
105 // RFC 3454, section 6, requirement 2
106 if (r && l)
108 throw new StringprepException(StringprepException.BIDI_BOTHRAL);
111 // RFC 3454, section 6, requirement 3
112 if (r)
114 if (!contains(s[0], RFC3454.D1) || !contains(s[s.Length - 1], RFC3454.D1))
116 throw new StringprepException(StringprepException.BIDI_LTRAL);
120 return s.ToString();
124 * Characters prohibited by RFC3920 nodeprep that aren't defined as
125 * part of the RFC3454 tables.
127 private static char [] RFC3920_NODEPREP_PROHIBIT = new char [] {
128 '\u0022', '\u0026', '\'', '\u002F',
129 '\u003A', '\u003C', '\u003E', '\u0040'
132 /// <summary> Preps a node name according to the Stringprep profile defined in
133 /// RFC3920. Unassigned code points are not allowed.
134 /// *
135 /// </summary>
136 /// <param name="input">the node name to prep.
137 /// </param>
138 /// <returns> the prepped node name.
139 /// @throws StringprepException If the node name cannot be prepped
140 /// with this profile.
141 /// @throws NullPointerException If the node name is null.
142 ///
143 /// </returns>
144 public static System.String nodeprep(System.String input)
146 return nodeprep(input, false);
149 /// <summary> Preps a node name according to the Stringprep profile defined in
150 /// RFC3920.
151 /// *
152 /// </summary>
153 /// <param name="input">the node name to prep.
154 /// </param>
155 /// <param name="allowUnassigned">true if the node name may contain
156 /// unassigned code points.
157 /// </param>
158 /// <returns> the prepped node name.
159 /// @throws StringprepException If the node name cannot be prepped
160 /// with this profile.
161 /// @throws NullPointerException If the node name is null.
162 ///
163 /// </returns>
164 public static System.String nodeprep(System.String input, bool allowUnassigned)
166 if (input == null)
168 throw new System.NullReferenceException();
171 System.Text.StringBuilder s = new System.Text.StringBuilder(input);
173 if (!allowUnassigned && contains(s, RFC3454.A1))
175 throw new StringprepException(StringprepException.CONTAINS_UNASSIGNED);
178 filter(s, RFC3454.B1);
179 map(s, RFC3454.B2search, RFC3454.B2replace);
181 s = new System.Text.StringBuilder(NFKC.normalizeNFKC(s.ToString()));
183 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))
185 // Table C.9 only contains code points > 0xFFFF which Java
186 // doesn't handle
187 throw new StringprepException(StringprepException.CONTAINS_PROHIBITED);
190 // Bidi handling
191 bool r = contains(s, RFC3454.D1);
192 bool l = contains(s, RFC3454.D2);
194 // RFC 3454, section 6, requirement 1: already handled above (table C.8)
196 // RFC 3454, section 6, requirement 2
197 if (r && l)
199 throw new StringprepException(StringprepException.BIDI_BOTHRAL);
202 // RFC 3454, section 6, requirement 3
203 if (r)
205 if (!contains(s[0], RFC3454.D1) || !contains(s[s.Length - 1], RFC3454.D1))
207 throw new StringprepException(StringprepException.BIDI_LTRAL);
211 return s.ToString();
214 /// <summary> Preps a resource name according to the Stringprep profile defined
215 /// in RFC3920. Unassigned code points are not allowed.
216 /// *
217 /// </summary>
218 /// <param name="input">the resource name to prep.
219 /// </param>
220 /// <returns> the prepped node name.
221 /// @throws StringprepException If the resource name cannot be prepped
222 /// with this profile.
223 /// @throws NullPointerException If the resource name is null.
224 ///
225 /// </returns>
226 public static System.String resourceprep(System.String input)
228 return resourceprep(input, false);
231 /// <summary> Preps a resource name according to the Stringprep profile defined
232 /// in RFC3920.
233 /// *
234 /// </summary>
235 /// <param name="input">the resource name to prep.
236 /// </param>
237 /// <param name="allowUnassigned">true if the resource name may contain
238 /// unassigned code points.
239 /// </param>
240 /// <returns> the prepped node name.
241 /// @throws StringprepException If the resource name cannot be prepped
242 /// with this profile.
243 /// @throws NullPointerException If the resource name is null.
244 ///
245 /// </returns>
246 public static System.String resourceprep(System.String input, bool allowUnassigned)
248 if (input == null)
250 throw new System.NullReferenceException();
253 System.Text.StringBuilder s = new System.Text.StringBuilder(input);
255 if (!allowUnassigned && contains(s, RFC3454.A1))
257 throw new StringprepException(StringprepException.CONTAINS_UNASSIGNED);
260 filter(s, RFC3454.B1);
262 s = new System.Text.StringBuilder(NFKC.normalizeNFKC(s.ToString()));
264 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))
266 // Table C.9 only contains code points > 0xFFFF which Java
267 // doesn't handle
268 throw new StringprepException(StringprepException.CONTAINS_PROHIBITED);
271 // Bidi handling
272 bool r = contains(s, RFC3454.D1);
273 bool l = contains(s, RFC3454.D2);
275 // RFC 3454, section 6, requirement 1: already handled above (table C.8)
277 // RFC 3454, section 6, requirement 2
278 if (r && l)
280 throw new StringprepException(StringprepException.BIDI_BOTHRAL);
283 // RFC 3454, section 6, requirement 3
284 if (r)
286 if (!contains(s[0], RFC3454.D1) || !contains(s[s.Length - 1], RFC3454.D1))
288 throw new StringprepException(StringprepException.BIDI_LTRAL);
292 return s.ToString();
295 internal static bool contains(System.Text.StringBuilder s, char[] p)
297 for (int i = 0; i < p.Length; i++)
299 char c = p[i];
300 for (int j = 0; j < s.Length; j++)
302 if (c == s[j])
304 return true;
308 return false;
311 internal static bool contains(System.Text.StringBuilder s, char[][] p)
313 for (int i = 0; i < p.Length; i++)
315 char[] r = p[i];
316 if (1 == r.Length)
318 char c = r[0];
319 for (int j = 0; j < s.Length; j++)
321 if (c == s[j])
323 return true;
327 else if (2 == r.Length)
329 char f = r[0];
330 char t = r[1];
331 for (int j = 0; j < s.Length; j++)
333 if (f <= s[j] && t >= s[j])
335 return true;
340 return false;
343 internal static bool contains(char c, char[][] p)
345 for (int i = 0; i < p.Length; i++)
347 char[] r = p[i];
348 if (1 == r.Length)
350 if (c == r[0])
352 return true;
355 else if (2 == r.Length)
357 char f = r[0];
358 char t = r[1];
359 if (f <= c && t >= c)
361 return true;
365 return false;
368 internal static void filter(System.Text.StringBuilder s, char[] f)
370 for (int i = 0; i < f.Length; i++)
372 char c = f[i];
374 int j = 0;
375 while (j < s.Length)
377 if (c == s[j])
379 //s.deleteCharAt(j);
380 s.Remove(j, 1);
382 else
384 j++;
390 internal static void filter(System.Text.StringBuilder s, char[][] f)
392 for (int i = 0; i < f.Length; i++)
394 char[] r = f[i];
396 if (1 == r.Length)
398 char c = r[0];
400 int j = 0;
401 while (j < s.Length)
403 if (c == s[j])
405 //s.deleteCharAt(j);
406 s.Remove(j, 1);
408 else
410 j++;
414 else if (2 == r.Length)
416 char from = r[0];
417 char to = r[1];
419 int j = 0;
420 while (j < s.Length)
422 if (from <= s[j] && to >= s[j])
424 //s.deleteCharAt(j);
425 s.Remove(j, 1);
427 else
429 j++;
436 internal static void map(System.Text.StringBuilder s, char[] search, System.String[] replace)
438 for (int i = 0; i < search.Length; i++)
440 char c = search[i];
442 int j = 0;
443 while (j < s.Length)
445 if (c == s[j])
447 //s.deleteCharAt(j);
448 s.Remove(j, 1);
449 if (null != replace[i])
451 s.Insert(j, replace[i]);
452 j += replace[i].Length - 1;
455 else
457 j++;