Removed untyped contructor from ComponentRegistration and add a protected setter.
[castle.git] / MonoRail / Castle.MonoRail.Framework / Helpers / TextHelper.cs
blob50d43497561eec8aec83bd4cd673cea74d424be0
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.MonoRail.Framework.Helpers
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.Globalization;
21 using System.Text;
22 using System.Text.RegularExpressions;
24 /// <summary>
25 /// Provides methods for working with strings and grammar. At the moment,
26 /// it contains the ToSentence overloads.
27 /// </summary>
28 public class TextHelper : AbstractHelper
30 #region Constructors
31 /// <summary>
32 /// Initializes a new instance of the <see cref="TextHelper"/> class.
33 /// </summary>
34 public TextHelper() { }
35 /// <summary>
36 /// Initializes a new instance of the <see cref="TextHelper"/> class.
37 /// setting the Controller, Context and ControllerContext.
38 /// </summary>
39 /// <param name="engineContext">The engine context.</param>
40 public TextHelper(IEngineContext engineContext) : base(engineContext) { }
41 #endregion
43 /// <summary>
44 /// Default word connector
45 /// </summary>
46 public const string DefaultConnector = "and";
48 /// <summary>
49 /// Converts the special characters to HTML.
50 /// </summary>
51 /// <param name="content">The content.</param>
52 /// <returns></returns>
53 public string ConvertSpecialCharsToHtml(string content)
55 if (string.IsNullOrEmpty(content))
57 return string.Empty;
60 return HtmlEncode(content).
61 Replace("\r\n", "<br />").
62 Replace("\r", "<br />").
63 Replace("\n", "<br />").
64 Replace("\t", "&nbsp;");
67 /// <summary>
68 /// Formats the specified string as a phone number, varying according to the culture.
69 /// </summary>
70 /// <param name="phone">The phone number to format.</param>
71 /// <returns></returns>
72 public string FormatPhone(string phone)
74 if (string.IsNullOrEmpty(phone)) return string.Empty;
75 if (phone.Length <= 3) return phone;
76 if (phone.IndexOfAny(new char[] { '(', '-', '.' }) != -1) return phone;
78 PhoneFormatter formatter = new PhoneFormatter();
79 return formatter.Format(phone);
82 /// <summary>
83 /// Converts a camelized text to words. For instance:
84 /// <c>FileWriter</c> is converted to <c>File Writer</c>
85 /// </summary>
86 /// <param name="pascalText">Content in pascal case</param>
87 /// <returns></returns>
88 public static string PascalCaseToWord(string pascalText)
90 if (pascalText == null) throw new ArgumentNullException("pascalText");
91 if (pascalText == string.Empty) return string.Empty;
93 StringBuilder sbText = new StringBuilder(pascalText.Length + 4);
95 char[] chars = pascalText.ToCharArray();
97 sbText.Append(chars[0]);
99 for(int i=1; i < chars.Length; i++)
101 char c = chars[i];
103 if (Char.IsUpper(c))
105 sbText.Append(' ');
108 sbText.Append(c);
111 return sbText.ToString();
114 /// <summary>
115 /// Builds a phrase listing a series of strings with with proper sentence semantics,
116 /// i.e. separating elements with &quot;, &quot; and prefacing the last element with
117 /// the specified <paramref name="connector"/>.
118 /// </summary>
119 /// <param name="elements">Collection with items to use in the sentence.</param>
120 /// <param name="connector">String to preface the last element.</param>
121 /// <returns>String suitable for use in a sentence.</returns>
122 /// <remarks>Calling <c>ToSentence( elements, "y" )</c> results in:
123 /// <code>
124 /// element1, element2 y element3
125 /// </code>
126 /// <para>If <paramref name="elements"/> is not an array of strings, each element will be
127 /// converted to string through <see cref="object.ToString"/>.</para>
128 /// </remarks>
129 /// <example>This example shows how to use <b>ToSentence</b>:
130 /// <code>
131 /// $TextHelper.ToSentence( elements, "y" )
132 /// </code>
133 /// </example>
134 public string ToSentence(ICollection elements, string connector)
136 return ToSentence(elements, connector, true);
139 /// <summary>
140 /// Builds a phrase listing a series of strings with with proper sentence semantics,
141 /// i.e. separating elements with &quot;, &quot; and prefacing the last element with
142 /// &quot; and &quot;.
143 /// </summary>
144 /// <param name="elements">Collection with items to use in the sentence.</param>
145 /// <param name="skipLastComma">True to skip the comma before the connector, false to include it.</param>
146 /// <returns>String suitable for use in a sentence.</returns>
147 /// <remarks>Calling <c>ToSentence( elements, false )</c> results in:
148 /// <code>
149 /// element1, element2, and element3
150 /// </code>
151 /// <para>If <paramref name="elements"/> is not an array of strings, each element will be
152 /// converted to string through <see cref="object.ToString"/>.</para>
153 /// </remarks>
154 /// <example>This example shows how to use <b>ToSentence</b>:
155 /// <code>
156 /// $TextHelper.ToSentence( elements, false )
157 /// </code>
158 /// </example>
159 public string ToSentence(ICollection elements, bool skipLastComma)
161 return ToSentence(elements, DefaultConnector, skipLastComma);
164 /// <summary>
165 /// Builds a phrase listing a series of strings with with proper sentence semantics,
166 /// i.e. separating elements with &quot;, &quot; and prefacing the last element with
167 /// &quot; and &quot;.
168 /// </summary>
169 /// <param name="elements">Collection with items to use in the sentence.</param>
170 /// <returns>String suitable for use in a sentence.</returns>
171 /// <remarks>Calling <c>ToSentence( elements )</c> results in:
172 /// <code>
173 /// element1, element2 and element3
174 /// </code>
175 /// <para>If <paramref name="elements"/> is not an array of strings, each element will be
176 /// converted to string through <see cref="object.ToString"/>.</para>
177 /// </remarks>
178 /// <example>This example shows how to use <b>ToSentence</b>:
179 /// <code>
180 /// $TextHelper.ToSentence( elements )
181 /// </code>
182 /// </example>
183 public string ToSentence(ICollection elements)
185 if (elements == null)
187 throw new ArgumentNullException("elements");
190 return ToSentence(elements, DefaultConnector, true);
193 /// <summary>
194 /// Builds a phrase listing a series of strings with with proper sentence semantics,
195 /// i.e. separating elements with &quot;, &quot; and prefacing the last element with
196 /// the specified <paramref name="connector"/>.
197 /// </summary>
198 /// <param name="elements">Collection with items to use in the sentence.</param>
199 /// <param name="connector">String to preface the last element.</param>
200 /// <param name="skipLastComma">True to skip the comma before the <paramref name="connector"/>, false to include it.</param>
201 /// <returns>String suitable for use in a sentence.</returns>
202 /// <remarks>Calling <c>ToSentence( elements, "y", false )</c> results in:
203 /// <code>
204 /// element1, element2, y element3
205 /// </code>
206 /// <para>If <paramref name="elements"/> is not an array of strings, each element will be
207 /// converted to string through <see cref="Object.ToString"/>.</para>
208 /// </remarks>
209 /// <example>This example shows how to use <b>ToSentence</b>:
210 /// <code>
211 /// $TextHelper.ToSentence( elements, "y", false )
212 /// </code>
213 /// </example>
214 public string ToSentence(ICollection elements, string connector, bool skipLastComma)
216 string[] array = elements as string[];
217 if (array == null)
219 array = new string[elements.Count];
220 IEnumerator enumerator = elements.GetEnumerator();
221 for(int i = 0; i < elements.Count; i++)
223 enumerator.MoveNext();
224 array[i] = enumerator.Current.ToString();
227 return ToSentence(array, connector, skipLastComma);
230 /// <summary>
231 /// Builds a phrase listing a series of strings with with proper sentence semantics,
232 /// i.e. separating elements with &quot;, &quot; and prefacing the last element with
233 /// the specified <paramref name="connector"/>.
234 /// </summary>
235 /// <param name="elements">Array of strings with items to use in the sentence.</param>
236 /// <param name="connector">String to preface the last element.</param>
237 /// <param name="skipLastComma">True to skip the comma before the <paramref name="connector"/>, false to include it.</param>
238 /// <returns>String suitable for use in a sentence.</returns>
239 /// <remarks>Calling <c>ToSentence( elements, "y", false )</c> results in:
240 /// <code>
241 /// element1, element2, y element3
242 /// </code>
243 /// </remarks>
244 /// <example>This example shows how to use <b>ToSentence</b>:
245 /// <code>
246 /// $TextHelper.ToSentence( elements, "y", false )
247 /// </code>
248 /// </example>
249 public string ToSentence(string[] elements, string connector, bool skipLastComma)
251 switch(elements.Length)
253 case 0:
255 return String.Empty;
257 case 1:
259 return elements[0];
261 case 2:
263 return String.Format("{0} {1} {2}", elements[0], connector, elements[1]);
265 default:
267 String[] allButLast = new String[elements.Length - 1];
269 Array.Copy(elements, allButLast, elements.Length - 1);
271 return String.Format("{0}{1} {2} {3}",
272 String.Join(", ", allButLast),
273 skipLastComma ? "" : ",",
274 connector,
275 elements[elements.Length - 1]);
280 /// <summary>
281 /// Shortens a text to the specified length and wraps it into a span-
282 /// element that has the title-property with the full text associated.
283 /// This is convenient for displaying properties in tables that might
284 /// have very much content (desription fields etc.) without destroying
285 /// the table's layout.
286 /// Due to the title-property of the surrounding span-element, the full
287 /// text is displayed in the browser while hovering over the shortened
288 /// text.
289 /// </summary>
290 /// <param name="text">The text to display</param>
291 /// <param name="maxLength">The maximum number of character to display</param>
292 /// <returns>The generated HTML</returns>
293 public string Fold(string text, int maxLength)
295 // Empty text
296 if (text == null) return "";
298 // maxLenght <= 0 switches off folding
299 // Determine whether text must be cut
300 if (maxLength <= 0 || text.Length < maxLength) return text;
302 StringBuilder caption = new StringBuilder();
303 foreach(string word in text.Split())
305 if (caption.Length + word.Length + 1 > maxLength - 1) break;
306 if (caption.Length > 0) caption.Append(" "); // Adding space
307 caption.Append(word);
310 caption.Append("&hellip;");
311 return string.Format("<span title=\"{1}\">{0}</span>", caption, text.Replace("\"", "&quot;"));
314 /// <summary>
315 /// Provides methods for formatting telephone numbers based on region.
316 /// </summary>
317 /// <remarks>At present, formats for USA and Brazil are defined. These need to be expanded.
318 /// </remarks>
319 /// <example>
320 /// In C# code:
321 /// <code>
322 /// PhoneFormatter formatter = new PhoneFormatter();
323 /// string officePhone = formatter.Format(contact.OfficePhone);
324 /// string homePhone = formatter.Format(contact.HomePhone);
325 /// </code>
326 /// Using in a view:
327 /// <code>
328 ///PropertyBag["phoneformatter"] = new PhoneFormatter();
329 ///
330 ///#if ($OfficePhone)
331 /// &lt;h3&gt;Office Phone: $phoneformatter.Format($OfficePhone)&lt;/h3&gt;
332 ///#end
333 /// </code>
334 /// </example>
335 public class PhoneFormatter
337 private readonly IEnumerable<FormatPair> formats;
339 /// <summary>
340 /// Initializes a new instance of the <see cref="PhoneFormatter"/> class for the specified region.
341 /// </summary>
342 /// <param name="name">The name or ISO 3166 code for region.</param>
343 /// <exception cref="ArgumentException"><paramref name="name"/> is not a valid country/region name or specific culture name.</exception>
344 /// <exception cref="ArgumentNullException"><paramref name="name"/> is null reference (<b>Nothing</b> in Visual Basic).</exception>
345 public PhoneFormatter(string name) : this(new RegionInfo(name))
349 /// <summary>
350 /// Initializes a new instance of the PhoneFormatter class for the region used by the current thread.
351 /// </summary>
352 public PhoneFormatter() : this((RegionInfo)null)
356 /// <summary>
357 /// Initializes a new instance of the PhoneFormatter class for the specified region
358 /// </summary>
359 /// <param name="rinfo">The Region </param>
360 public PhoneFormatter(RegionInfo rinfo)
362 if (rinfo == null)
364 rinfo = RegionInfo.CurrentRegion;
367 formats = PhoneFormatterFactory.GetFormatPairs(rinfo);
370 /// <summary>
371 /// Formats the specified string as a phone number, varying according to the culture.
372 /// </summary>
373 /// <param name="orig">The string to format</param>
374 /// <returns>string, formatted phone number.</returns>
375 public string Format(string orig)
377 // Remove all characters besides numbers and letters.
378 Regex strip = new Regex(@"[^\w]", RegexOptions.IgnoreCase);
379 string stripped = strip.Replace(orig, "");
381 foreach (FormatPair testcase in formats)
383 Regex convert = new Regex(testcase.Pattern);
384 Match match = convert.Match(stripped);
385 if (match.Success)
387 return match.Result(testcase.Formatted);
390 return orig;
394 internal class FormatPair
396 public string Pattern;
397 public string Formatted;
398 /// <summary>
399 /// Initializes a new instance of the FormatPair class.
400 /// </summary>
401 /// <param name="pattern"></param>
402 /// <param name="formatted"></param>
403 public FormatPair(string pattern, string formatted)
405 Pattern = pattern;
406 Formatted = formatted;
410 internal static class PhoneFormatterFactory
412 public static IEnumerable<FormatPair> GetFormatPairs(RegionInfo rinfo)
414 List<FormatPair> pairs = new List<FormatPair>();
416 // This section
417 // a) Needs to be expanded to other regions.
418 // b) should be cached.
419 // c) Should be handled in a more robust fashion. (resource files? Xml file?)
421 switch (rinfo.TwoLetterISORegionName)
423 case "US": // United States
424 pairs.Add(new FormatPair(@"^(\w\w\d)(\d\d\d\d)$", @"$1-$2"));
425 pairs.Add(new FormatPair(@"^(\d\d\d)(\w\w\d)(\d\d\d\d)$", @"($1) $2-$3"));
426 pairs.Add(new FormatPair(@"^1(\d\d\d)(\w\w\d)(\d\d\d\d)$", @"+1 ($1) $2-$3"));
427 pairs.Add(new FormatPair(@"^(\d\d\d)(\w\w\d)(\d\d\d\d)x?(\d+)$", @"($1) $2-$3 ext. $4"));
428 pairs.Add(new FormatPair(@"^1(\d\d\d)(\w\w\d)(\d\d\d\d)x?(\d+)$", @"+1 ($1) $2-$3 ext. $4"));
429 break;
431 case "BR": // Brazil
432 pairs.Add(new FormatPair(@"^(\d\d\d\d)(\d\d\d\d)$", @"$1-$2"));
433 pairs.Add(new FormatPair(@"^(\d\d)(\d\d\d\d)(\d\d\d\d)$", @"($1) $2-$3"));
434 pairs.Add(new FormatPair(@"^(0\d\d)(\d\d\d\d)(\d\d\d\d)$", @"($1) $2-$3"));
435 pairs.Add(new FormatPair(@"^(xx\d\d)(\d\d\d\d)(\d\d\d\d)$", @"($1) $2-$3"));
436 pairs.Add(new FormatPair(@"^(0xx\d\d)(\d\d\d\d)(\d\d\d\d)$", @"($1) $2-$3"));
437 pairs.Add(new FormatPair(@"^55(\d\d)(\d\d\d\d)(\d\d\d\d)$", @"+55 ($1) $2-$3"));
438 pairs.Add(new FormatPair(@"^55(0\d\d)(\d\d\d\d)(\d\d\d\d)$", @"+55 ($1) $2-$3"));
439 pairs.Add(new FormatPair(@"^55(xx\d\d)(\d\d\d\d)(\d\d\d\d)$", @"+55 ($1) $2-$3"));
440 pairs.Add(new FormatPair(@"^55(0xx\d\d)(\d\d\d\d)(\d\d\d\d)$", @"+55 ($1) $2-$3"));
442 pairs.Add(new FormatPair(@"^(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"$1-$2 Reul. $3"));
443 pairs.Add(new FormatPair(@"^(\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"($1) $2-$3 Reul. $4"));
444 pairs.Add(new FormatPair(@"^(0\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"($1) $2-$3 Reul. $4"));
445 pairs.Add(new FormatPair(@"^(xx\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"($1) $2-$3 Reul. $4"));
446 pairs.Add(new FormatPair(@"^(0xx\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"($1) $2-$3 Reul. $4"));
447 pairs.Add(new FormatPair(@"^55(\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"+55 ($1) $2-$3 Reul. $4"));
448 pairs.Add(new FormatPair(@"^55(0\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"+55 ($1) $2-$3 Reul. $4"));
449 pairs.Add(new FormatPair(@"^55(xx\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"+55 ($1) $2-$3 Reul. $4"));
450 pairs.Add(new FormatPair(@"^55(0xx\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"+55 ($1) $2-$3 Reul. $4"));
451 break;
453 default:
454 throw new ArgumentOutOfRangeException("rinfo", "Telephone formats for given RegionInfo have not yet been defined.");
457 return pairs;