PageRenderTime 34ms CodeModel.GetById 14ms app.highlight 17ms RepoModel.GetById 0ms app.codeStats 1ms

/IronPython_Main/Languages/IronPython/IronPython.Modules/_locale.cs

#
C# | 296 lines | 227 code | 43 blank | 26 comment | 18 complexity | b70534ca547b5e8276db538347bf50f1 MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
  6 * copy of the license can be found in the License.html file at the root of this distribution. If 
  7 * you cannot locate the  Apache License, Version 2.0, please send an email to 
  8 * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
  9 * by the terms of the Apache License, Version 2.0.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15
 16using System;
 17using System.Globalization;
 18using System.Runtime.CompilerServices;
 19using System.Runtime.InteropServices;
 20using IronPython.Runtime;
 21using IronPython.Runtime.Exceptions;
 22using IronPython.Runtime.Types;
 23using Microsoft.Scripting;
 24using Microsoft.Scripting.Runtime;
 25using Microsoft.Scripting.Utils;
 26
 27[assembly: PythonModule("_locale", typeof(IronPython.Modules.PythonLocale))]
 28namespace IronPython.Modules {
 29    public static class PythonLocale {
 30        public const string __doc__ = "Provides access for querying and manipulating the current locale settings";
 31
 32        private static readonly object _localeKey = new object();
 33
 34        [SpecialName]
 35        public static void PerformModuleReload(PythonContext/*!*/ context, PythonDictionary/*!*/ dict) {
 36            EnsureLocaleInitialized(context);
 37            context.EnsureModuleException("_localeerror", dict, "Error", "_locale");
 38        }
 39
 40        internal static void EnsureLocaleInitialized(PythonContext context) {
 41            if (!context.HasModuleState(_localeKey)) {
 42                context.SetModuleState(_localeKey, new LocaleInfo(context));
 43            }
 44        }
 45
 46        public const int CHAR_MAX = 127;
 47        public const int LC_ALL = (int)LocaleCategories.All;
 48        public const int LC_COLLATE = (int)LocaleCategories.Collate;
 49        public const int LC_CTYPE = (int)LocaleCategories.CType;
 50        public const int LC_MONETARY = (int)LocaleCategories.Monetary;
 51        public const int LC_NUMERIC = (int)LocaleCategories.Numeric;
 52        public const int LC_TIME = (int)LocaleCategories.Time;
 53
 54        internal static string PreferredEncoding {
 55            get {
 56#if !SILVERLIGHT    // No ANSICodePage in Silverlight
 57                return "cp" + CultureInfo.CurrentCulture.TextInfo.ANSICodePage.ToString();
 58#else
 59                return "";
 60#endif
 61            }
 62        }
 63
 64        [Documentation("gets the default locale tuple")]
 65        public static object _getdefaultlocale() {            
 66            return PythonTuple.MakeTuple(
 67                CultureInfo.CurrentCulture.Name.Replace('-', '_').Replace(' ', '_'), 
 68                PreferredEncoding
 69            );
 70        }
 71
 72        [Documentation(@"gets the locale's convetions table.  
 73
 74The conventions table is a dictionary that contains information on how to use 
 75the locale for numeric and monetary formatting")]
 76        public static object localeconv(CodeContext/*!*/ context) {
 77            return GetLocaleInfo(context).GetConventionsTable();
 78        }
 79
 80        [Documentation(@"Sets the current locale for the given category.
 81
 82LC_ALL:       sets locale for all options below
 83LC_COLLATE:   sets locale for collation (strcoll and strxfrm) only
 84LC_CTYPE:     sets locale for CType [unused]
 85LC_MONETARY:  sets locale for the monetary functions (localeconv())
 86LC_NUMERIC:   sets the locale for numeric functions (slocaleconv())
 87LC_TIME:      sets the locale for time functions [unused]
 88
 89If locale is None then the current setting is returned.
 90")]
 91        public static object setlocale(CodeContext/*!*/ context, int category, [DefaultParameterValue(null)]string locale) {
 92            LocaleInfo li = GetLocaleInfo(context);
 93            if (locale == null) {
 94                return li.GetLocale(context, category);
 95            }
 96
 97            return li.SetLocale(context, category, locale);
 98        }
 99
100        [Documentation("compares two strings using the current locale")]
101        public static int strcoll(CodeContext/*!*/ context, string string1, string string2) {
102            return GetLocaleInfo(context).Collate.CompareInfo.Compare(string1, string2, CompareOptions.None);
103        }
104
105        [Documentation(@"returns a transformed string that can be compared using the built-in cmp.
106        
107Currently returns the string unmodified")]
108        public static object strxfrm(string @string) {
109            return @string;
110        }
111
112        private enum LocaleCategories {
113            All = 0,
114            Collate = 1,
115            CType = 2,
116            Monetary = 3,
117            Numeric = 4,
118            Time = 5,
119        }
120
121        internal class LocaleInfo {
122            private readonly PythonContext _context;
123            private PythonDictionary conv;
124
125            public LocaleInfo(PythonContext context) {
126                _context = context;
127            }
128
129            public CultureInfo Collate {
130                get { return _context.CollateCulture; }
131                set { _context.CollateCulture = value; }
132            }
133
134            public CultureInfo CType {
135                get { return _context.CTypeCulture; }
136                set { _context.CTypeCulture= value; }
137            }
138            
139            public CultureInfo Time {
140                get { return _context.TimeCulture; }
141                set { _context.TimeCulture = value; }
142            }
143
144            public CultureInfo Monetary {
145                get { return _context.MonetaryCulture; }
146                set { _context.MonetaryCulture = value; }
147            }
148            
149            public CultureInfo Numeric {
150                get { return _context.NumericCulture; }
151                set { _context.NumericCulture = value; }
152            }
153
154            public override string ToString() {
155                return base.ToString();
156            }
157
158            public PythonDictionary GetConventionsTable() {
159                CreateConventionsDict();
160
161                return conv;
162            }
163
164            public string SetLocale(CodeContext/*!*/ context, int category, string locale) {
165                switch ((LocaleCategories)category) {
166                    case LocaleCategories.All:
167                        SetLocale(context, LC_COLLATE, locale);
168                        SetLocale(context, LC_CTYPE, locale);
169                        SetLocale(context, LC_MONETARY, locale);
170                        SetLocale(context, LC_NUMERIC, locale);
171                        return SetLocale(context, LC_TIME, locale);
172                    case LocaleCategories.Collate:
173                        return CultureToName(Collate = LocaleToCulture(context, locale));
174                    case LocaleCategories.CType:
175                        return CultureToName(CType = LocaleToCulture(context, locale));                        
176                    case LocaleCategories.Time:
177                        return CultureToName(Time = LocaleToCulture(context, locale));                        
178                    case LocaleCategories.Monetary:
179                        Monetary = LocaleToCulture(context, locale);
180                        conv = null;
181                        return CultureToName(Monetary);
182                    case LocaleCategories.Numeric:
183                        Numeric = LocaleToCulture(context, locale);
184                        conv = null;
185                        return CultureToName(Numeric);
186                    default:
187                        throw PythonExceptions.CreateThrowable(_localeerror(context), "unknown locale category");
188                }
189
190            }
191
192            public string GetLocale(CodeContext/*!*/ context, int category) {
193                switch ((LocaleCategories)category) {
194                    case LocaleCategories.All:
195                        if (Collate == CType &&
196                            Collate == Time &&
197                            Collate == Monetary &&
198                            Collate == Numeric) {
199                            // they're all the same, return only 1 name
200                            goto case LocaleCategories.Collate;
201                        }
202
203                        // return them all...
204                        return String.Format("LC_COLLATE={0};LC_CTYPE={1};LC_MONETARY={2};LC_NUMERIC={3};LC_TIME={4}",
205                            GetLocale(context, LC_COLLATE),
206                            GetLocale(context, LC_CTYPE),
207                            GetLocale(context, LC_MONETARY),
208                            GetLocale(context, LC_NUMERIC),
209                            GetLocale(context, LC_TIME));
210                    case LocaleCategories.Collate: return CultureToName(Collate);
211                    case LocaleCategories.CType: return CultureToName(CType);
212                    case LocaleCategories.Time: return CultureToName(Time);
213                    case LocaleCategories.Monetary: return CultureToName(Monetary);
214                    case LocaleCategories.Numeric: return CultureToName(Numeric);
215                    default:
216                        throw PythonExceptions.CreateThrowable(_localeerror(context), "unknown locale category");
217                }
218            }
219
220            public string CultureToName(CultureInfo culture) {
221                if (culture == PythonContext.CCulture) {
222                    return "C";
223                }
224                
225                return culture.Name.Replace('-', '_');
226            }
227
228            private CultureInfo LocaleToCulture(CodeContext/*!*/ context, string locale) {
229                if (locale == "C") {
230                    return PythonContext.CCulture;
231                }
232
233                locale = locale.Replace('_', '-');
234
235                try {
236                    return StringUtils.GetCultureInfo(locale);
237                } catch (ArgumentException) {
238                    throw PythonExceptions.CreateThrowable(_localeerror(context), String.Format("unknown locale: {0}", locale));
239                }
240            }
241
242            /// <summary>
243            /// Popupates the given directory w/ the locale information from the given
244            /// CultureInfo.
245            /// </summary>
246            private void CreateConventionsDict() {
247                conv = new PythonDictionary();
248
249                conv["decimal_point"] = Numeric.NumberFormat.NumberDecimalSeparator;
250                conv["grouping"] = GroupsToList(Numeric.NumberFormat.NumberGroupSizes);
251                conv["thousands_sep"] = Numeric.NumberFormat.NumberGroupSeparator;
252
253                conv["mon_decimal_point"] = Monetary.NumberFormat.CurrencyDecimalSeparator;
254                conv["mon_thousands_sep"] = Monetary.NumberFormat.CurrencyGroupSeparator;
255                conv["mon_grouping"] = GroupsToList(Monetary.NumberFormat.CurrencyGroupSizes);
256                conv["int_curr_symbol"] = Monetary.NumberFormat.CurrencySymbol;
257                conv["currency_symbol"] = Monetary.NumberFormat.CurrencySymbol;
258                conv["frac_digits"] = Monetary.NumberFormat.CurrencyDecimalDigits;
259                conv["int_frac_digits"] = Monetary.NumberFormat.CurrencyDecimalDigits;
260                conv["positive_sign"] = Monetary.NumberFormat.PositiveSign;
261                conv["negative_sign"] = Monetary.NumberFormat.NegativeSign;
262
263                conv["p_sign_posn"] = Monetary.NumberFormat.CurrencyPositivePattern;
264                conv["n_sign_posn"] = Monetary.NumberFormat.CurrencyNegativePattern;
265            }
266
267            private static List GroupsToList(int[] groups) {
268                // .NET: values from 0-9, if the last digit is zero, remaining digits
269                // go ungrouped, otherwise they're grouped based upon the last value.
270
271                // locale: ends in CHAR_MAX if no further grouping is performed, ends in
272                // zero if the last group size is repeatedly used.
273                List res = new List(groups);
274                if (groups.Length > 0 && groups[groups.Length - 1] == 0) {
275                    // replace zero w/ CHAR_MAX, no further grouping is performed
276                    res[res.__len__() - 1] = CHAR_MAX;
277                } else {
278                    // append 0 to indicate we should repeatedly use the last one
279                    res.AddNoLock(0);
280                }
281
282                return res;
283            }
284        }
285
286        internal static LocaleInfo/*!*/ GetLocaleInfo(CodeContext/*!*/ context) {
287            EnsureLocaleInitialized(PythonContext.GetContext(context));
288
289            return (LocaleInfo)PythonContext.GetContext(context).GetModuleState(_localeKey);
290        }        
291
292        private static PythonType _localeerror(CodeContext/*!*/ context) {
293            return (PythonType)PythonContext.GetContext(context).GetModuleState("_localeerror");
294        }
295    }
296}