1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
18 using System
.Collections
;
19 using System
.Collections
.Generic
;
20 using System
.Globalization
;
22 using System
.Text
.RegularExpressions
;
25 /// Provides methods for working with strings and grammar. At the moment,
26 /// it contains the ToSentence overloads.
28 public class TextHelper
: AbstractHelper
33 /// Initializes a new instance of the <see cref="TextHelper"/> class.
40 /// Initializes a new instance of the <see cref="TextHelper"/> class.
41 /// setting the Controller, Context and ControllerContext.
43 /// <param name="engineContext">The engine context.</param>
44 public TextHelper(IEngineContext engineContext
) : base(engineContext
)
51 /// Default word connector
53 public const string DefaultConnector
= "and";
56 /// Converts the special characters to HTML.
58 /// <param name="content">The content.</param>
59 /// <returns></returns>
60 public string ConvertSpecialCharsToHtml(string content
)
62 if (string.IsNullOrEmpty(content
))
67 return HtmlEncode(content
).
68 Replace("\r\n", "<br />").
69 Replace("\r", "<br />").
70 Replace("\n", "<br />").
71 Replace("\t", " ");
75 /// Formats the specified string as a phone number, varying according to the culture.
77 /// <param name="phone">The phone number to format.</param>
78 /// <returns></returns>
79 public string FormatPhone(string phone
)
81 if (string.IsNullOrEmpty(phone
))
85 if (phone
.Length
<= 3)
89 if (phone
.IndexOfAny(new char[] {'(', '-', '.'}
) != -1)
94 PhoneFormatter formatter
= new PhoneFormatter();
95 return formatter
.Format(phone
);
99 /// Converts a camelized text to words. For instance:
100 /// <c>FileWriter</c> is converted to <c>File Writer</c>
102 /// <param name="pascalText">Content in pascal case</param>
103 /// <returns></returns>
104 public static string PascalCaseToWord(string pascalText
)
106 if (pascalText
== null)
108 throw new ArgumentNullException("pascalText");
110 if (pascalText
== string.Empty
)
115 StringBuilder sbText
= new StringBuilder(pascalText
.Length
+ 4);
117 char[] chars
= pascalText
.ToCharArray();
119 sbText
.Append(chars
[0]);
121 for(int i
= 1; i
< chars
.Length
; i
++)
133 return sbText
.ToString();
137 /// Builds a phrase listing a series of strings with with proper sentence semantics,
138 /// i.e. separating elements with ", " and prefacing the last element with
139 /// the specified <paramref name="connector"/>.
141 /// <param name="elements">Collection with items to use in the sentence.</param>
142 /// <param name="connector">String to preface the last element.</param>
143 /// <returns>String suitable for use in a sentence.</returns>
144 /// <remarks>Calling <c>ToSentence( elements, "y" )</c> results in:
146 /// element1, element2 y element3
148 /// <para>If <paramref name="elements"/> is not an array of strings, each element will be
149 /// converted to string through <see cref="object.ToString"/>.</para>
151 /// <example>This example shows how to use <b>ToSentence</b>:
153 /// $TextHelper.ToSentence( elements, "y" )
156 public static string ToSentence(ICollection elements
, string connector
)
158 return ToSentence(elements
, connector
, true);
162 /// Builds a phrase listing a series of strings with with proper sentence semantics,
163 /// i.e. separating elements with ", " and prefacing the last element with
164 /// " and ".
166 /// <param name="elements">Collection with items to use in the sentence.</param>
167 /// <param name="skipLastComma">True to skip the comma before the connector, false to include it.</param>
168 /// <returns>String suitable for use in a sentence.</returns>
169 /// <remarks>Calling <c>ToSentence( elements, false )</c> results in:
171 /// element1, element2, and element3
173 /// <para>If <paramref name="elements"/> is not an array of strings, each element will be
174 /// converted to string through <see cref="object.ToString"/>.</para>
176 /// <example>This example shows how to use <b>ToSentence</b>:
178 /// $TextHelper.ToSentence( elements, false )
181 public static string ToSentence(ICollection elements
, bool skipLastComma
)
183 return ToSentence(elements
, DefaultConnector
, skipLastComma
);
187 /// Builds a phrase listing a series of strings with with proper sentence semantics,
188 /// i.e. separating elements with ", " and prefacing the last element with
189 /// " and ".
191 /// <param name="elements">Collection with items to use in the sentence.</param>
192 /// <returns>String suitable for use in a sentence.</returns>
193 /// <remarks>Calling <c>ToSentence( elements )</c> results in:
195 /// element1, element2 and element3
197 /// <para>If <paramref name="elements"/> is not an array of strings, each element will be
198 /// converted to string through <see cref="object.ToString"/>.</para>
200 /// <example>This example shows how to use <b>ToSentence</b>:
202 /// $TextHelper.ToSentence( elements )
205 public static string ToSentence(ICollection elements
)
207 if (elements
== null)
209 throw new ArgumentNullException("elements");
212 return ToSentence(elements
, DefaultConnector
, true);
216 /// Builds a phrase listing a series of strings with with proper sentence semantics,
217 /// i.e. separating elements with ", " and prefacing the last element with
218 /// " and ".
220 /// <param name="elements">Collection with items to use in the sentence.</param>
221 /// <returns>String suitable for use in a sentence.</returns>
222 /// <remarks>Calling <c>ToSentence( elements )</c> results in:
224 /// element1, element2 and element3
226 /// <para>If <paramref name="elements"/> is not an array of strings, each element will be
227 /// converted to string through <see cref="object.ToString"/>.</para>
229 /// <example>This example shows how to use <b>ToSentence</b>:
231 /// $TextHelper.ToSentence( elements )
234 public static string ToSentence(IList elements
)
236 if (elements
== null)
238 throw new ArgumentNullException("elements");
241 return ToSentence(elements
, DefaultConnector
, true);
245 /// Builds a phrase listing a series of strings with with proper sentence semantics,
246 /// i.e. separating elements with ", " and prefacing the last element with
247 /// the specified <paramref name="connector"/>.
249 /// <param name="elements">Collection with items to use in the sentence.</param>
250 /// <param name="connector">String to preface the last element.</param>
251 /// <param name="skipLastComma">True to skip the comma before the <paramref name="connector"/>, false to include it.</param>
252 /// <returns>String suitable for use in a sentence.</returns>
253 /// <remarks>Calling <c>ToSentence( elements, "y", false )</c> results in:
255 /// element1, element2, y element3
257 /// <para>If <paramref name="elements"/> is not an array of strings, each element will be
258 /// converted to string through <see cref="Object.ToString"/>.</para>
260 /// <example>This example shows how to use <b>ToSentence</b>:
262 /// $TextHelper.ToSentence( elements, "y", false )
265 public static string ToSentence(ICollection elements
, string connector
, bool skipLastComma
)
267 string[] array
= elements
as string[];
270 array
= new string[elements
.Count
];
271 IEnumerator enumerator
= elements
.GetEnumerator();
272 for(int i
= 0; i
< elements
.Count
; i
++)
274 enumerator
.MoveNext();
275 array
[i
] = enumerator
.Current
.ToString();
278 return ToSentence(array
, connector
, skipLastComma
);
282 /// Builds a phrase listing a series of strings with with proper sentence semantics,
283 /// i.e. separating elements with ", " and prefacing the last element with
284 /// the specified <paramref name="connector"/>.
286 /// <param name="elements">Array of strings with items to use in the sentence.</param>
287 /// <param name="connector">String to preface the last element.</param>
288 /// <param name="skipLastComma">True to skip the comma before the <paramref name="connector"/>, false to include it.</param>
289 /// <returns>String suitable for use in a sentence.</returns>
290 /// <remarks>Calling <c>ToSentence( elements, "y", false )</c> results in:
292 /// element1, element2, y element3
295 /// <example>This example shows how to use <b>ToSentence</b>:
297 /// $TextHelper.ToSentence( elements, "y", false )
300 public static string ToSentence(string[] elements
, string connector
, bool skipLastComma
)
302 switch(elements
.Length
)
314 return elements
[0] + " " + connector
+ " " + elements
[1];
318 String
[] allButLast
= new String
[elements
.Length
- 1];
320 Array
.Copy(elements
, allButLast
, elements
.Length
- 1);
323 String
.Join(", ", allButLast
) + (skipLastComma
? "" : ",") + " " +
325 elements
[elements
.Length
- 1];
331 /// Shortens a text to the specified length and wraps it into a span-
332 /// element that has the title-property with the full text associated.
333 /// This is convenient for displaying properties in tables that might
334 /// have very much content (desription fields etc.) without destroying
335 /// the table's layout.
336 /// Due to the title-property of the surrounding span-element, the full
337 /// text is displayed in the browser while hovering over the shortened
340 /// <param name="text">The text to display</param>
341 /// <param name="maxLength">The maximum number of character to display</param>
342 /// <returns>The generated HTML</returns>
343 public string Fold(string text
, int maxLength
)
351 // maxLenght <= 0 switches off folding
352 // Determine whether text must be cut
353 if (maxLength
<= 0 || text
.Length
< maxLength
)
358 StringBuilder caption
= new StringBuilder();
359 foreach(string word
in text
.Split())
361 if (caption
.Length
+ word
.Length
+ 1 > maxLength
- 1)
365 if (caption
.Length
> 0)
367 caption
.Append(" "); // Adding space
369 caption
.Append(word
);
372 caption
.Append("…");
373 return string.Format("<span title=\"{1}\">{0}</span>", caption
, text
.Replace("\"", """));
377 /// Provides methods for formatting telephone numbers based on region.
379 /// <remarks>At present, formats for USA and Brazil are defined. These need to be expanded.
384 /// PhoneFormatter formatter = new PhoneFormatter();
385 /// string officePhone = formatter.Format(contact.OfficePhone);
386 /// string homePhone = formatter.Format(contact.HomePhone);
390 ///PropertyBag["phoneformatter"] = new PhoneFormatter();
392 ///#if ($OfficePhone)
393 /// <h3>Office Phone: $phoneformatter.Format($OfficePhone)</h3>
397 public class PhoneFormatter
399 private readonly IEnumerable
<FormatPair
> formats
;
402 /// Initializes a new instance of the <see cref="PhoneFormatter"/> class for the specified region.
404 /// <param name="name">The name or ISO 3166 code for region.</param>
405 /// <exception cref="ArgumentException"><paramref name="name"/> is not a valid country/region name or specific culture name.</exception>
406 /// <exception cref="ArgumentNullException"><paramref name="name"/> is null reference (<b>Nothing</b> in Visual Basic).</exception>
407 public PhoneFormatter(string name
) : this(new RegionInfo(name
))
412 /// Initializes a new instance of the PhoneFormatter class for the region used by the current thread.
414 public PhoneFormatter() : this((RegionInfo
) null)
419 /// Initializes a new instance of the PhoneFormatter class for the specified region
421 /// <param name="rinfo">The Region </param>
422 public PhoneFormatter(RegionInfo rinfo
)
426 rinfo
= RegionInfo
.CurrentRegion
;
429 formats
= PhoneFormatterFactory
.GetFormatPairs(rinfo
);
433 /// Formats the specified string as a phone number, varying according to the culture.
435 /// <param name="orig">The string to format</param>
436 /// <returns>string, formatted phone number.</returns>
437 public string Format(string orig
)
439 // Remove all characters besides numbers and letters.
440 Regex strip
= new Regex(@"[^\w]", RegexOptions
.IgnoreCase
);
441 string stripped
= strip
.Replace(orig
, "");
443 foreach(FormatPair testcase
in formats
)
445 Regex convert
= new Regex(testcase
.Pattern
);
446 Match match
= convert
.Match(stripped
);
449 return match
.Result(testcase
.Formatted
);
456 internal class FormatPair
458 public string Pattern
;
459 public string Formatted
;
462 /// Initializes a new instance of the FormatPair class.
464 /// <param name="pattern"></param>
465 /// <param name="formatted"></param>
466 public FormatPair(string pattern
, string formatted
)
469 Formatted
= formatted
;
473 internal static class PhoneFormatterFactory
475 public static IEnumerable
<FormatPair
> GetFormatPairs(RegionInfo rinfo
)
477 List
<FormatPair
> pairs
= new List
<FormatPair
>();
480 // a) Needs to be expanded to other regions.
481 // b) should be cached.
482 // c) Should be handled in a more robust fashion. (resource files? Xml file?)
484 switch(rinfo
.TwoLetterISORegionName
)
486 case "US": // United States
487 pairs
.Add(new FormatPair(@"^(\w\w\d)(\d\d\d\d)$", @"$1-$2"));
488 pairs
.Add(new FormatPair(@"^(\d\d\d)(\w\w\d)(\d\d\d\d)$", @"($1) $2-$3"));
489 pairs
.Add(new FormatPair(@"^1(\d\d\d)(\w\w\d)(\d\d\d\d)$", @"+1 ($1) $2-$3"));
490 pairs
.Add(new FormatPair(@"^(\d\d\d)(\w\w\d)(\d\d\d\d)x?(\d+)$", @"($1) $2-$3 ext. $4"));
491 pairs
.Add(new FormatPair(@"^1(\d\d\d)(\w\w\d)(\d\d\d\d)x?(\d+)$", @"+1 ($1) $2-$3 ext. $4"));
495 pairs
.Add(new FormatPair(@"^(\d\d\d\d)(\d\d\d\d)$", @"$1-$2"));
496 pairs
.Add(new FormatPair(@"^(\d\d)(\d\d\d\d)(\d\d\d\d)$", @"($1) $2-$3"));
497 pairs
.Add(new FormatPair(@"^(0\d\d)(\d\d\d\d)(\d\d\d\d)$", @"($1) $2-$3"));
498 pairs
.Add(new FormatPair(@"^(xx\d\d)(\d\d\d\d)(\d\d\d\d)$", @"($1) $2-$3"));
499 pairs
.Add(new FormatPair(@"^(0xx\d\d)(\d\d\d\d)(\d\d\d\d)$", @"($1) $2-$3"));
500 pairs
.Add(new FormatPair(@"^55(\d\d)(\d\d\d\d)(\d\d\d\d)$", @"+55 ($1) $2-$3"));
501 pairs
.Add(new FormatPair(@"^55(0\d\d)(\d\d\d\d)(\d\d\d\d)$", @"+55 ($1) $2-$3"));
502 pairs
.Add(new FormatPair(@"^55(xx\d\d)(\d\d\d\d)(\d\d\d\d)$", @"+55 ($1) $2-$3"));
503 pairs
.Add(new FormatPair(@"^55(0xx\d\d)(\d\d\d\d)(\d\d\d\d)$", @"+55 ($1) $2-$3"));
505 pairs
.Add(new FormatPair(@"^(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"$1-$2 Reul. $3"));
506 pairs
.Add(new FormatPair(@"^(\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"($1) $2-$3 Reul. $4"));
507 pairs
.Add(new FormatPair(@"^(0\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"($1) $2-$3 Reul. $4"));
508 pairs
.Add(new FormatPair(@"^(xx\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"($1) $2-$3 Reul. $4"));
509 pairs
.Add(new FormatPair(@"^(0xx\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"($1) $2-$3 Reul. $4"));
510 pairs
.Add(new FormatPair(@"^55(\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"+55 ($1) $2-$3 Reul. $4"));
511 pairs
.Add(new FormatPair(@"^55(0\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"+55 ($1) $2-$3 Reul. $4"));
512 pairs
.Add(new FormatPair(@"^55(xx\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"+55 ($1) $2-$3 Reul. $4"));
513 pairs
.Add(new FormatPair(@"^55(0xx\d\d)(\d\d\d\d)(\d\d\d\d)r?(\d+)$", @"+55 ($1) $2-$3 Reul. $4"));
517 throw new ArgumentOutOfRangeException("rinfo", "Telephone formats for given RegionInfo have not yet been defined.");