Sync with TP.
[libidn.git] / csharp / Stringprep.cs
blob3cc5bd74f7be100e4d52ea533bb8afacbd9d1ef9
1 /// <summary>
2 /// *
3 /// Author: Alexander Gnauck AG-Software, mailto:gnauck@ag-software.de
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;
24 using System.Text;
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 string NamePrep(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 string NamePrep(string input, bool allowUnassigned)
73 if (input == null)
75 throw new System.NullReferenceException();
78 StringBuilder s = new 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 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 string NodePrep(string input)
146 return NodePrep(input, false);
149 /// <summary>
150 /// Preps a node name according to the Stringprep profile defined in RFC3920.
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 string NodePrep(string input, bool allowUnassigned)
165 if (input == null)
167 throw new System.NullReferenceException();
170 StringBuilder s = new 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 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>
214 /// Preps a resource name according to the Stringprep profile defined
215 /// in RFC3920. Unassigned code points are not allowed.
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 string ResourcePrep(string input)
227 return ResourcePrep(input, false);
230 /// <summary>
231 /// Preps a resource name according to the Stringprep profile defined
232 /// in RFC3920.
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>
240 /// 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 string ResourcePrep(string input, bool allowUnassigned)
248 if (input == null)
250 throw new System.NullReferenceException();
253 StringBuilder s = new StringBuilder(input);
255 if (!allowUnassigned && Contains(s, RFC3454.A1))
257 throw new StringprepException(StringprepException.CONTAINS_UNASSIGNED);
260 Filter(s, RFC3454.B1);
262 s = new 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(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(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(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(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(StringBuilder s, char[] search, 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++;