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
32 /// Initializes a new instance of the <see cref="TextHelper"/> class.
34 public TextHelper() { }
36 /// Initializes a new instance of the <see cref="TextHelper"/> class.
37 /// setting the Controller, Context and ControllerContext.
39 /// <param name="engineContext">The engine context.</param>
40 public TextHelper(IEngineContext engineContext
) : base(engineContext
) { }
44 /// Default word connector
46 public const string DefaultConnector
= "and";
49 /// Converts the special characters to HTML.
51 /// <param name="content">The content.</param>
52 /// <returns></returns>
53 public string ConvertSpecialCharsToHtml(string content
)
55 if (string.IsNullOrEmpty(content
))
60 return HtmlEncode(content
).
61 Replace("\r\n", "<br />").
62 Replace("\r", "<br />").
63 Replace("\n", "<br />").
64 Replace("\t", " ");
68 /// Formats the specified string as a phone number, varying according to the culture.
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
);
83 /// Converts a camelized text to words. For instance:
84 /// <c>FileWriter</c> is converted to <c>File Writer</c>
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
++)
111 return sbText
.ToString();
115 /// Builds a phrase listing a series of strings with with proper sentence semantics,
116 /// i.e. separating elements with ", " and prefacing the last element with
117 /// the specified <paramref name="connector"/>.
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:
124 /// element1, element2 y element3
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>
129 /// <example>This example shows how to use <b>ToSentence</b>:
131 /// $TextHelper.ToSentence( elements, "y" )
134 public string ToSentence(ICollection elements
, string connector
)
136 return ToSentence(elements
, connector
, true);
140 /// Builds a phrase listing a series of strings with with proper sentence semantics,
141 /// i.e. separating elements with ", " and prefacing the last element with
142 /// " and ".
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:
149 /// element1, element2, and element3
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>
154 /// <example>This example shows how to use <b>ToSentence</b>:
156 /// $TextHelper.ToSentence( elements, false )
159 public string ToSentence(ICollection elements
, bool skipLastComma
)
161 return ToSentence(elements
, DefaultConnector
, skipLastComma
);
165 /// Builds a phrase listing a series of strings with with proper sentence semantics,
166 /// i.e. separating elements with ", " and prefacing the last element with
167 /// " and ".
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:
173 /// element1, element2 and element3
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>
178 /// <example>This example shows how to use <b>ToSentence</b>:
180 /// $TextHelper.ToSentence( elements )
183 public string ToSentence(ICollection elements
)
185 if (elements
== null)
187 throw new ArgumentNullException("elements");
190 return ToSentence(elements
, DefaultConnector
, true);
194 /// Builds a phrase listing a series of strings with with proper sentence semantics,
195 /// i.e. separating elements with ", " and prefacing the last element with
196 /// the specified <paramref name="connector"/>.
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:
204 /// element1, element2, y element3
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>
209 /// <example>This example shows how to use <b>ToSentence</b>:
211 /// $TextHelper.ToSentence( elements, "y", false )
214 public string ToSentence(ICollection elements
, string connector
, bool skipLastComma
)
216 string[] array
= elements
as string[];
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
);
231 /// Builds a phrase listing a series of strings with with proper sentence semantics,
232 /// i.e. separating elements with ", " and prefacing the last element with
233 /// the specified <paramref name="connector"/>.
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:
241 /// element1, element2, y element3
244 /// <example>This example shows how to use <b>ToSentence</b>:
246 /// $TextHelper.ToSentence( elements, "y", false )
249 public string ToSentence(string[] elements
, string connector
, bool skipLastComma
)
251 switch(elements
.Length
)
263 return String
.Format("{0} {1} {2}", elements
[0], connector
, elements
[1]);
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
? "" : ",",
275 elements
[elements
.Length
- 1]);
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
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
)
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("…");
311 return string.Format("<span title=\"{1}\">{0}</span>", caption
, text
.Replace("\"", """));
315 /// Provides methods for formatting telephone numbers based on region.
317 /// <remarks>At present, formats for USA and Brazil are defined. These need to be expanded.
322 /// PhoneFormatter formatter = new PhoneFormatter();
323 /// string officePhone = formatter.Format(contact.OfficePhone);
324 /// string homePhone = formatter.Format(contact.HomePhone);
328 ///PropertyBag["phoneformatter"] = new PhoneFormatter();
330 ///#if ($OfficePhone)
331 /// <h3>Office Phone: $phoneformatter.Format($OfficePhone)</h3>
335 public class PhoneFormatter
337 private readonly IEnumerable
<FormatPair
> formats
;
340 /// Initializes a new instance of the <see cref="PhoneFormatter"/> class for the specified region.
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
))
350 /// Initializes a new instance of the PhoneFormatter class for the region used by the current thread.
352 public PhoneFormatter() : this((RegionInfo
)null)
357 /// Initializes a new instance of the PhoneFormatter class for the specified region
359 /// <param name="rinfo">The Region </param>
360 public PhoneFormatter(RegionInfo rinfo
)
364 rinfo
= RegionInfo
.CurrentRegion
;
367 formats
= PhoneFormatterFactory
.GetFormatPairs(rinfo
);
371 /// Formats the specified string as a phone number, varying according to the culture.
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
);
387 return match
.Result(testcase
.Formatted
);
394 internal class FormatPair
396 public string Pattern
;
397 public string Formatted
;
399 /// Initializes a new instance of the FormatPair class.
401 /// <param name="pattern"></param>
402 /// <param name="formatted"></param>
403 public FormatPair(string pattern
, string formatted
)
406 Formatted
= formatted
;
410 internal static class PhoneFormatterFactory
412 public static IEnumerable
<FormatPair
> GetFormatPairs(RegionInfo rinfo
)
414 List
<FormatPair
> pairs
= new List
<FormatPair
>();
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"));
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"));
454 throw new ArgumentOutOfRangeException("rinfo", "Telephone formats for given RegionInfo have not yet been defined.");