4 // Copyright (C) 2004 Novell, Inc.
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
28 using System
.Collections
;
29 using System
.Globalization
;
36 namespace Beagle
.Util
{
38 public class StringFu
{
40 private StringFu () { }
// class is static
42 private const String timeFormat
= "yyyyMMddHHmmss";
44 static public string DateTimeToString (DateTime dt
)
46 return dt
.ToString (timeFormat
);
49 static public DateTime
StringToDateTime (string str
)
51 if (str
== null || str
== "")
52 return new DateTime ();
54 return DateTime
.ParseExact (str
, timeFormat
, CultureInfo
.CurrentCulture
);
57 static public string DateTimeToFuzzy (DateTime dt
)
59 DateTime today
= DateTime
.Today
;
60 TimeSpan sinceToday
= today
- dt
;
62 string date
= null, time
= null;
64 if (sinceToday
.TotalDays
<= 0)
65 date
= Catalog
.GetString ("Today");
66 else if (sinceToday
.TotalDays
< 1)
67 date
= Catalog
.GetString ("Yesterday");
68 else if (today
.Year
== dt
.Year
)
69 date
= dt
.ToString (Catalog
.GetString ("MMM d"));
71 date
= dt
.ToString (Catalog
.GetString ("MMM d, yyyy"));
73 time
= dt
.ToString (Catalog
.GetString ("h:mm tt"));
77 if (date
!= null && time
!= null)
78 /* Translators: {0} is a date (e.g. 'Today' or 'Apr 23'), {1} is the time */
79 fuzzy
= String
.Format (Catalog
.GetString ("{0}, {1}"), date
, time
);
80 else if (date
!= null)
88 public static string DateTimeToPrettyString (DateTime date
)
90 DateTime now
= DateTime
.Now
;
91 string short_time
= date
.ToShortTimeString ();
93 if (date
.Year
== now
.Year
) {
94 if (date
.DayOfYear
== now
.DayOfYear
) {
95 /* To translators: {0} is the time of the day, eg. 13:45 */
96 return String
.Format (Catalog
.GetString ("Today, {0}"), short_time
);
97 } else if (date
.DayOfYear
== now
.DayOfYear
- 1) {
98 /* To translators: {0} is the time of the day, eg. 13:45 */
99 return String
.Format (Catalog
.GetString ("Yesterday, {0}"), short_time
);
100 } else if (date
.DayOfYear
> now
.DayOfYear
- 6) {
101 /* To translators: {0} is the number of days that have passed, {1} is the time of the day, eg. 13:45 */
102 return String
.Format (Catalog
.GetString ("{0} days ago, {1}"),
103 now
.DayOfYear
- date
.DayOfYear
,
106 return date
.ToString (Catalog
.GetString ("MMMM d, h:mm tt"));
110 return date
.ToString (Catalog
.GetString ("MMMM d yyyy, h:mm tt"));
113 public static string DurationToPrettyString (DateTime end_time
, DateTime start_time
)
115 TimeSpan span
= end_time
- start_time
;
117 string span_str
= "";
119 if (span
.Hours
> 0) {
120 span_str
= String
.Format (Catalog
.GetPluralString ("{0} hour", "{0} hours", span
.Hours
), span
.Hours
);
122 if (span
.Minutes
> 0)
126 if (span
.Minutes
> 0) {
127 span_str
+= String
.Format (Catalog
.GetPluralString ("{0} minute", "{0} minutes", span
.Minutes
), span
.Minutes
);
134 static public string FileLengthToString (long len
)
136 const long oneMb
= 1024*1024;
139 return "*BadLength*";
142 /* Translators: {0} is a file size in bytes */
143 return String
.Format (Catalog
.GetString ("{0} bytes"), len
);
146 /* Translators: {0} is a file size in kilobytes */
147 return String
.Format (Catalog
.GetString ("{0:0.0} KB"), len
/(double)1024);
149 /* Translators: {0} is a file size in megabytes */
150 return String
.Format (Catalog
.GetString ("{0:0.0} MB"), len
/(double)oneMb
);
153 // FIXME: This is pretty inefficient
154 static public string[] FuzzySplit (string line
)
158 // Replace non-alphanumeric characters with spaces
159 StringBuilder builder
= new StringBuilder (line
.Length
);
160 for (i
= 0; i
< line
.Length
; ++i
) {
161 if (Char
.IsLetterOrDigit (line
[i
]))
162 builder
.Append (line
[i
]);
164 builder
.Append (" ");
166 line
= builder
.ToString ();
168 // Inject whitespace on all case changes except
169 // from upper to lower.
172 while (i
< line
.Length
) {
174 if (Char
.IsUpper (line
[i
]))
176 else if (Char
.IsLower (line
[i
]))
181 if (prevCase
!= thisCase
182 && !(prevCase
== +1 && thisCase
== -1)) {
183 line
= line
.Substring (0, i
) + " " + line
.Substring (i
);
192 // Filter out empty parts
193 ArrayList partsArray
= new ArrayList ();
194 foreach (string str
in line
.Split (' ')) {
196 partsArray
.Add (str
);
199 // Assemble the array to return
200 string[] parts
= new string [partsArray
.Count
];
201 for (i
= 0; i
< partsArray
.Count
; ++i
)
202 parts
[i
] = (string) partsArray
[i
];
206 // Match strings against patterns that are allowed to contain
207 // glob-style * wildcards.
208 // This recursive implementation is not particularly efficient,
209 // and probably will fail for weird corner cases.
210 static public bool GlobMatch (string pattern
, string str
)
214 else if (pattern
.StartsWith ("**"))
215 return GlobMatch (pattern
.Substring (1), str
);
216 else if (str
== "" && pattern
!= "")
219 int i
= pattern
.IndexOf ('*');
221 return pattern
== str
;
222 else if (i
> 0 && i
< str
.Length
)
223 return pattern
.Substring (0, i
) == str
.Substring (0, i
)
224 && GlobMatch (pattern
.Substring (i
), str
.Substring (i
));
226 return GlobMatch (pattern
.Substring (1), str
.Substring (1))
227 || GlobMatch (pattern
.Substring (1), str
)
228 || GlobMatch (pattern
, str
.Substring (1));
233 // FIXME: how do we do this operation in a culture-neutral way?
234 static public string[] SplitQuoted (string str
)
236 char[] specialChars
= new char [2] { ' ', '"' }
;
238 ArrayList array
= new ArrayList ();
241 while ((i
= str
.IndexOfAny (specialChars
)) != -1) {
242 if (str
[i
] == ' ') {
244 array
.Add (str
.Substring (0, i
));
245 str
= str
.Substring (i
+1);
246 } else if (str
[i
] == '"') {
247 int j
= str
.IndexOf ('"', i
+1);
249 array
.Add (str
.Substring (0, i
));
251 if (i
+1 < str
.Length
)
252 array
.Add (str
.Substring (i
+1));
256 array
.Add (str
.Substring (i
+1, j
-i
-1));
257 str
= str
.Substring (j
+1);
264 string [] retval
= new string [array
.Count
];
265 for (i
= 0; i
< array
.Count
; ++i
)
266 retval
[i
] = (string) array
[i
];
270 static public bool ContainsWhiteSpace (string str
)
272 foreach (char c
in str
)
273 if (char.IsWhiteSpace (c
))
278 static char[] CharsToQuote
= { ';', '?', ':', '@', '&', '=', '$', ',', '#', '%', '"', ' ' }
;
280 static public string HexEscape (string str
)
282 StringBuilder builder
= new StringBuilder ();
285 while ((i
= str
.IndexOfAny (CharsToQuote
)) != -1) {
287 builder
.Append (str
.Substring (0, i
));
288 builder
.Append (Uri
.HexEscape (str
[i
]));
289 str
= str
.Substring (i
+1);
291 builder
.Append (str
);
293 return builder
.ToString ();
296 // Translate all %xx codes into real characters
297 static public string HexUnescape (string str
)
300 while ((i
= str
.IndexOf ('%', pos
)) != -1) {
302 char unescaped
= UriFu
.HexUnescape (str
, ref pos
);
303 str
= str
.Remove (i
, 3);
304 str
= str
.Insert (i
, new String(unescaped
, 1));
310 static public string PathToQuotedFileUri (string path
)
312 path
= Path
.GetFullPath (path
);
313 return Uri
.UriSchemeFile
+ Uri
.SchemeDelimiter
+ HexEscape (path
);
316 // These strings should never be exposed to the user.
318 static object uidLock
= new object ();
319 static public string GetUniqueId ()
323 Random r
= new Random ();
328 return string.Format ("{0}-{1}-{2}-{3}",
329 Environment
.GetEnvironmentVariable ("USER"),
330 Environment
.GetEnvironmentVariable ("HOST"),
336 static string [] replacements
= new string [] {
337 "&", "<", ">", """, "'",
340 static private StringBuilder cachedStringBuilder
;
341 static private char QuoteChar
= '\"';
343 private static bool IsInvalid (int ch
)
367 static public string EscapeStringForHtml (string source
, bool skipQuotations
)
371 int count
= source
.Length
;
373 for (int i
= 0; i
< count
; i
++) {
374 switch (source
[i
]) {
375 case '&': pos
= 0; break;
376 case '<': pos
= 1; break;
377 case '>': pos
= 2; break;
379 if (skipQuotations
) continue;
380 if (QuoteChar
== '\'') continue;
383 if (skipQuotations
) continue;
384 if (QuoteChar
== '\"') continue;
387 if (skipQuotations
) continue;
390 if (skipQuotations
) continue;
393 if (IsInvalid (source
[i
])) {
394 invalid
= source
[i
];
401 if (cachedStringBuilder
== null)
402 cachedStringBuilder
= new StringBuilder
404 cachedStringBuilder
.Append (source
.Substring (start
, i
- start
));
406 cachedStringBuilder
.Append ("&#x");
407 if (invalid
< (char) 255)
408 cachedStringBuilder
.Append (((int) invalid
).ToString ("X02", CultureInfo
.InvariantCulture
));
410 cachedStringBuilder
.Append (((int) invalid
).ToString ("X04", CultureInfo
.InvariantCulture
));
411 cachedStringBuilder
.Append (";");
414 cachedStringBuilder
.Append (replacements
[pos
]);
419 else if (start
< count
)
420 cachedStringBuilder
.Append (source
.Substring (start
, count
- start
));
421 string s
= cachedStringBuilder
.ToString ();
422 cachedStringBuilder
.Length
= 0;
426 static public string CleanupInvalidXmlCharacters (string str
)
431 int len
= str
.Length
;
433 // Find the first invalid character in the string
435 while (i
< len
&& ! IsInvalid (str
[i
]))
438 // If the string doesn't contain invalid characters,
443 // Otherwise copy the first chunk, then go through
444 // character by character looking for more invalid stuff.
446 char [] char_array
= new char[len
];
448 for (int j
= 0; j
< i
; ++j
)
449 char_array
[j
] = str
[j
];
450 char_array
[i
] = ' ';
452 for (int j
= i
+1; j
< len
; ++j
) {
455 char_array
[j
] = ' ';
460 return new string (char_array
);
463 static public int CountWords (string str
, int max_words
)
468 bool last_was_white
= true;
470 for (int i
= 0; i
< str
.Length
; ++i
) {
471 if (Char
.IsWhiteSpace (str
[i
])) {
472 last_was_white
= true;
474 if (last_was_white
) {
476 if (max_words
> 0 && words
>= max_words
)
479 last_was_white
= false;
486 static public int CountWords (string str
)
488 return CountWords (str
, -1);