Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/4.0.3/EPPlus-4.0.3/Style/XmlAccess/ExcelNumberFormatXml.cs @ 14112

Last change on this file since 14112 was 12074, checked in by sraggl, 10 years ago

#2341: Added EPPlus-4.0.3 to ExtLibs

File size: 28.7 KB
Line 
1/*******************************************************************************
2 * You may amend and distribute as you like, but don't remove this header!
3 *
4 * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
5 * See http://www.codeplex.com/EPPlus for details.
6 *
7 * Copyright (C) 2011  Jan Källman
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
17 * See the GNU Lesser General Public License for more details.
18 *
19 * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
20 * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
21 *
22 * All code and executables are provided "as is" with no warranty either express or implied.
23 * The author accepts no liability for any damage or loss of business that this product may cause.
24 *
25 * Code change notes:
26 *
27 * Author             Change            Date
28 * ******************************************************************************
29 * Jan Källman                    Initial Release           2009-10-01
30 * Jan Källman    License changed GPL-->LGPL 2011-12-16
31 *******************************************************************************/
32using System;
33using System.Collections.Generic;
34using System.Text;
35using System.Xml;
36using System.Globalization;
37using System.Text.RegularExpressions;
38namespace OfficeOpenXml.Style.XmlAccess
39{
40    /// <summary>
41    /// Xml access class for number formats
42    /// </summary>
43    public sealed class ExcelNumberFormatXml : StyleXmlHelper
44    {
45        internal ExcelNumberFormatXml(XmlNamespaceManager nameSpaceManager) : base(nameSpaceManager)
46        {
47           
48        }       
49        internal ExcelNumberFormatXml(XmlNamespaceManager nameSpaceManager, bool buildIn): base(nameSpaceManager)
50        {
51            BuildIn = buildIn;
52        }
53        internal ExcelNumberFormatXml(XmlNamespaceManager nsm, XmlNode topNode) :
54            base(nsm, topNode)
55        {
56            _numFmtId = GetXmlNodeInt("@numFmtId");
57            _format = GetXmlNodeString("@formatCode");
58        }
59        public bool BuildIn { get; private set; }
60        int _numFmtId;
61//        const string idPath = "@numFmtId";
62        /// <summary>
63        /// Id for number format
64        ///
65        /// Build in ID's
66        ///
67        /// 0   General
68        /// 1   0
69        /// 2   0.00
70        /// 3   #,##0
71        /// 4   #,##0.00
72        /// 9   0%
73        /// 10  0.00%
74        /// 11  0.00E+00
75        /// 12  # ?/?
76        /// 13  # ??/??
77        /// 14  mm-dd-yy
78        /// 15  d-mmm-yy
79        /// 16  d-mmm
80        /// 17  mmm-yy
81        /// 18  h:mm AM/PM
82        /// 19  h:mm:ss AM/PM
83        /// 20  h:mm
84        /// 21  h:mm:ss
85        /// 22  m/d/yy h:mm
86        /// 37  #,##0 ;(#,##0)
87        /// 38  #,##0 ;[Red](#,##0)
88        /// 39  #,##0.00;(#,##0.00)
89        /// 40  #,##0.00;[Red](#,##0.00)
90        /// 45  mm:ss
91        /// 46  [h]:mm:ss
92        /// 47  mmss.0
93        /// 48  ##0.0E+0
94        /// 49  @
95        /// </summary>           
96        public int NumFmtId
97        {
98            get
99            {
100                return _numFmtId;
101            }
102            set
103            {
104                _numFmtId = value;
105            }
106        }
107        internal override string Id
108        {
109            get
110            {
111                return _format;
112            }
113        }
114        const string fmtPath = "@formatCode";
115        string _format = string.Empty;
116        public string Format
117        {
118            get
119            {
120                return _format;
121            }
122            set
123            {
124                _numFmtId = ExcelNumberFormat.GetFromBuildIdFromFormat(value);
125                _format = value;
126            }
127        }
128        internal string GetNewID(int NumFmtId, string Format)
129        {
130           
131            if (NumFmtId < 0)
132            {
133                NumFmtId = ExcelNumberFormat.GetFromBuildIdFromFormat(Format);               
134            }
135            return NumFmtId.ToString();
136        }
137
138        internal static void AddBuildIn(XmlNamespaceManager NameSpaceManager, ExcelStyleCollection<ExcelNumberFormatXml> NumberFormats)
139        {
140            NumberFormats.Add("General",new ExcelNumberFormatXml(NameSpaceManager,true){NumFmtId=0,Format="General"});
141            NumberFormats.Add("0", new ExcelNumberFormatXml(NameSpaceManager,true) { NumFmtId = 1, Format = "0" });
142            NumberFormats.Add("0.00", new ExcelNumberFormatXml(NameSpaceManager,true) { NumFmtId = 2, Format = "0.00" });
143            NumberFormats.Add("#,##0", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 3, Format = "#,##0" });
144            NumberFormats.Add("#,##0.00", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 4, Format = "#,##0.00" });
145            NumberFormats.Add("0%", new ExcelNumberFormatXml(NameSpaceManager,true) { NumFmtId = 9, Format = "0%" });
146            NumberFormats.Add("0.00%", new ExcelNumberFormatXml(NameSpaceManager,true) { NumFmtId = 10, Format = "0.00%" });
147            NumberFormats.Add("0.00E+00", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 11, Format = "0.00E+00" });
148            NumberFormats.Add("# ?/?", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 12, Format = "# ?/?" });
149            NumberFormats.Add("# ??/??", new ExcelNumberFormatXml(NameSpaceManager,true) { NumFmtId = 13, Format = "# ??/??" });
150            NumberFormats.Add("mm-dd-yy", new ExcelNumberFormatXml(NameSpaceManager,true) { NumFmtId = 14, Format = "mm-dd-yy" });
151            NumberFormats.Add("d-mmm-yy", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 15, Format = "d-mmm-yy" });
152            NumberFormats.Add("d-mmm", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 16, Format = "d-mmm" });
153            NumberFormats.Add("mmm-yy", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 17, Format = "mmm-yy" });
154            NumberFormats.Add("h:mm AM/PM", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 18, Format = "h:mm AM/PM" });
155            NumberFormats.Add("h:mm:ss AM/PM", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 19, Format = "h:mm:ss AM/PM" });
156            NumberFormats.Add("h:mm", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 20, Format = "h:mm" });
157            NumberFormats.Add("h:mm:ss", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 21, Format = "h:mm:ss" });
158            NumberFormats.Add("m/d/yy h:mm", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 22, Format = "m/d/yy h:mm" });
159            NumberFormats.Add("#,##0 ;(#,##0)", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 37, Format = "#,##0 ;(#,##0)" });
160            NumberFormats.Add("#,##0 ;[Red](#,##0)", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 38, Format = "#,##0 ;[Red](#,##0)" });
161            NumberFormats.Add("#,##0.00;(#,##0.00)", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 39, Format = "#,##0.00;(#,##0.00)" });
162            NumberFormats.Add("#,##0.00;[Red](#,##0.00)", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 40, Format = "#,##0.00;[Red](#,#)" });
163            NumberFormats.Add("mm:ss", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 45, Format = "mm:ss" });
164            NumberFormats.Add("[h]:mm:ss", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 46, Format = "[h]:mm:ss" });
165            NumberFormats.Add("mmss.0", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 47, Format = "mmss.0" });
166            NumberFormats.Add("##0.0", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 48, Format = "##0.0" });
167            NumberFormats.Add("@", new ExcelNumberFormatXml(NameSpaceManager, true) { NumFmtId = 49, Format = "@" });
168
169            NumberFormats.NextId = 164; //Start for custom formats.
170        }
171
172        internal override XmlNode CreateXmlNode(XmlNode topNode)
173        {
174            TopNode = topNode;
175            SetXmlNodeString("@numFmtId", NumFmtId.ToString());
176            SetXmlNodeString("@formatCode", Format);
177            return TopNode;
178        }
179
180        internal enum eFormatType
181        {
182            Unknown = 0,
183            Number = 1,
184            DateTime = 2,
185        }
186        ExcelFormatTranslator _translator = null;
187        internal ExcelFormatTranslator FormatTranslator
188        {
189            get
190            {
191                if (_translator == null)
192                {
193                    _translator = new ExcelFormatTranslator(Format, NumFmtId);
194                }
195                return _translator;
196            }
197        }
198        #region Excel --> .Net Format
199        internal class ExcelFormatTranslator
200        {
201            internal ExcelFormatTranslator(string format, int numFmtID)
202            {
203                if (numFmtID == 14)
204                {
205                    NetFormat = NetFormatForWidth = "d";
206                    NetTextFormat = NetTextFormatForWidth = "";
207                    DataType = eFormatType.DateTime;
208                }
209                else if (format.Equals("general",StringComparison.InvariantCultureIgnoreCase))
210                {
211                    NetFormat = NetFormatForWidth = "0.#####";
212                    NetTextFormat = NetTextFormatForWidth = "";
213                    DataType = eFormatType.Number;
214                }
215                else
216                {
217                    ToNetFormat(format, false);
218                    ToNetFormat(format, true);
219                }               
220            }
221            internal string NetTextFormat { get; private set; }
222            internal string NetFormat { get; private set; }
223            CultureInfo _ci = null;
224            internal CultureInfo Culture
225            {
226                get
227                {
228                    if (_ci == null)
229                    {
230                        return CultureInfo.CurrentCulture;
231                    }
232                    return _ci;
233                }
234                private set
235                {
236                    _ci = value;
237                }
238            }
239            internal eFormatType DataType { get; private set; }
240            internal string NetTextFormatForWidth { get; private set; }
241            internal string NetFormatForWidth { get; private set; }
242
243            //internal string FractionFormatInteger { get; private set; }
244            internal string FractionFormat { get; private set; }
245            //internal string FractionFormat2 { get; private set; }
246
247            private void ToNetFormat(string ExcelFormat, bool forColWidth)
248            {
249                DataType = eFormatType.Unknown;
250                int secCount = 0;
251                bool isText = false;
252                bool isBracket = false;
253                string bracketText = "";
254                bool prevBslsh = false;
255                bool useMinute = false;
256                bool prevUnderScore = false;
257                bool ignoreNext = false;
258                int fractionPos = -1;
259                string specialDateFormat = "";
260                bool containsAmPm = ExcelFormat.Contains("AM/PM");
261                List<int> lstDec=new List<int>();
262                StringBuilder sb = new StringBuilder();
263                Culture = null;
264                var format = "";
265                var text = "";
266                char clc;
267
268                if (containsAmPm)
269                {
270                    ExcelFormat = Regex.Replace(ExcelFormat, "AM/PM", "");
271                    DataType = eFormatType.DateTime;
272                }
273
274                for (int pos = 0; pos < ExcelFormat.Length; pos++)
275                {
276                    char c = ExcelFormat[pos];
277                    if (c == '"')
278                    {
279                        isText = !isText;
280                    }
281                    else
282                    {
283                        if (ignoreNext)
284                        {
285                            ignoreNext = false;
286                            continue;
287                        }
288                        else if (isText && !isBracket)
289                        {
290                            sb.Append(c);
291                        }
292                        else if (isBracket)
293                        {
294                            if (c == ']')
295                            {
296                                isBracket = false;
297                                if (bracketText[0] == '$')  //Local Info
298                                {
299                                    string[] li = Regex.Split(bracketText, "-");
300                                    if (li[0].Length > 1)
301                                    {
302                                        sb.Append("\"" + li[0].Substring(1, li[0].Length - 1) + "\"");     //Currency symbol
303                                    }
304                                    if (li.Length > 1)
305                                    {
306                                        if (li[1].Equals("f800", StringComparison.InvariantCultureIgnoreCase))
307                                        {
308                                            specialDateFormat = "D";
309                                        }
310                                        else if (li[1].Equals("f400", StringComparison.InvariantCultureIgnoreCase))
311                                        {
312                                            specialDateFormat = "T";
313                                        }
314                                        else
315                                        {
316                                            var num = int.Parse(li[1], NumberStyles.HexNumber);
317                                            try
318                                            {
319                                                Culture = CultureInfo.GetCultureInfo(num & 0xFFFF);
320                                            }
321                                            catch
322                                            {
323                                                Culture = null;
324                                            }
325                                        }
326                                    }
327                                }
328                                else if(bracketText[0]=='t')
329                                {
330                                    sb.Append("hh"); //TODO:This will not be correct for dates over 24H.
331                                }
332                                else if (bracketText[0] == 'h')
333                                {
334                                    specialDateFormat = "hh"; //TODO:This will not be correct for dates over 24H.
335                                }
336                            }
337                            else
338                            {
339                                bracketText += c;
340                            }
341                        }
342                        else if (prevUnderScore)
343                        {
344                            if (forColWidth)
345                            {
346                                sb.AppendFormat("\"{0}\"", c);
347                            }
348                            prevUnderScore = false;
349                        }
350                        else
351                        {
352                            if (c == ';') //We use first part (for positive only at this stage)
353                            {
354                                secCount++;
355                                if (DataType == eFormatType.DateTime || secCount == 3)
356                                {
357                                    //Add qoutes
358                                    SetDecimal(lstDec, sb);
359                                    lstDec = new List<int>();
360                                    format = sb.ToString();
361                                    sb = new StringBuilder();
362                                }
363                                else
364                                {
365                                    sb.Append(c);
366                                }
367                            }
368                            else
369                            {
370                                clc = c.ToString().ToLower(CultureInfo.InvariantCulture)[0];  //Lowercase character
371                                //Set the datetype
372                                if (DataType == eFormatType.Unknown)
373                                {
374                                    if (c == '0' || c == '#' || c == '.')
375                                    {
376                                        DataType = eFormatType.Number;
377                                    }
378                                    else if (clc == 'y' || clc == 'm' || clc == 'd' || clc == 'h' || clc == 'm' || clc == 's')
379                                    {
380                                        DataType = eFormatType.DateTime;
381                                    }
382                                }
383
384                                if (prevBslsh)
385                                {
386                                    sb.Append(c);
387                                    prevBslsh = false;
388                                }
389                                else if (c == '[')
390                                {
391                                    bracketText = "";
392                                    isBracket = true;
393                                }
394                                else if (c == '\\')
395                                {
396                                    prevBslsh = true;
397                                }
398                                else if (c == '0' ||
399                                    c == '#' ||
400                                    c == '.' ||
401                                    c == ',' ||
402                                    c == '%' ||
403                                    clc == 'd' ||
404                                    clc == 's')
405                                {
406                                    sb.Append(c);
407                                    if(c=='.')
408                                    {
409                                        lstDec.Add(sb.Length - 1);
410                                    }
411                                }
412                                else if (clc == 'h')
413                                {
414                                    if (containsAmPm)
415                                    {
416                                        sb.Append('h'); ;
417                                    }
418                                    else
419                                    {
420                                        sb.Append('H');
421                                    }
422                                    useMinute = true;
423                                }
424                                else if (clc == 'm')
425                                {
426                                    if (useMinute)
427                                    {
428                                        sb.Append('m');
429                                    }
430                                    else
431                                    {
432                                        sb.Append('M');
433                                    }
434                                }
435                                else if (c == '_') //Skip next but use for alignment
436                                {
437                                    prevUnderScore = true;
438                                }
439                                else if (c == '?')
440                                {
441                                    sb.Append(' ');
442                                }
443                                else if (c == '/')
444                                {
445                                    if (DataType == eFormatType.Number)
446                                    {
447                                        fractionPos = sb.Length;
448                                        int startPos = pos - 1;
449                                        while (startPos >= 0 &&
450                                                (ExcelFormat[startPos] == '?' ||
451                                                ExcelFormat[startPos] == '#' ||
452                                                ExcelFormat[startPos] == '0'))
453                                        {
454                                            startPos--;
455                                        }
456
457                                        if (startPos > 0)  //RemovePart
458                                            sb.Remove(sb.Length-(pos-startPos-1),(pos-startPos-1)) ;
459
460                                        int endPos = pos + 1;
461                                        while (endPos < ExcelFormat.Length &&
462                                                (ExcelFormat[endPos] == '?' ||
463                                                ExcelFormat[endPos] == '#' ||
464                                                (ExcelFormat[endPos] >= '0' && ExcelFormat[endPos]<= '9')))
465                                        {
466                                            endPos++;
467                                        }
468                                        pos = endPos;
469                                        if (FractionFormat != "")
470                                        {
471                                            FractionFormat = ExcelFormat.Substring(startPos+1, endPos - startPos-1);
472                                        }
473                                        sb.Append('?'); //Will be replaced later on by the fraction
474                                    }
475                                    else
476                                    {
477                                        sb.Append('/');
478                                    }
479                                }
480                                else if (c == '*')
481                                {
482                                    //repeat char--> ignore
483                                    ignoreNext = true;
484                                }
485                                else if (c == '@')
486                                {
487                                    sb.Append("{0}");
488                                }
489                                else
490                                {
491                                    sb.Append(c);
492                                }
493                            }
494                        }
495                    }
496                }
497
498                //Add qoutes
499                SetDecimal(lstDec, sb);
500
501                // AM/PM format
502                if (containsAmPm)
503                {
504                    format += "tt";
505                }
506
507
508                if (format == "")
509                    format = sb.ToString();
510                else
511                    text = sb.ToString();
512                if (specialDateFormat != "")
513                {
514                    format = specialDateFormat;
515                }
516
517                if (forColWidth)
518                {
519                    NetFormatForWidth = format;
520                    NetTextFormatForWidth = text;
521                }
522                else
523                {
524                    NetFormat = format;
525                    NetTextFormat = text;
526                }
527                if (Culture == null)
528                {
529                    Culture = CultureInfo.CurrentCulture;
530                }
531            }
532
533            private static void SetDecimal(List<int> lstDec, StringBuilder sb)
534            {
535                if (lstDec.Count > 1)
536                {
537                    for (int i = lstDec.Count - 1; i >= 0; i--)
538                    {
539                        sb.Insert(lstDec[i] + 1, '\'');
540                        sb.Insert(lstDec[i], '\'');
541                    }
542                }
543            }
544
545            internal string FormatFraction(double d)
546            {
547                int numerator, denomerator;
548
549                int intPart = (int)d;
550
551                string[] fmt = FractionFormat.Split('/');
552
553                int fixedDenominator;
554                if (!int.TryParse(fmt[1], out fixedDenominator))
555                {
556                    fixedDenominator = 0;
557                }
558               
559                if (d == 0 || double.IsNaN(d))
560                {
561                    if (fmt[0].Trim() == "" && fmt[1].Trim() == "")
562                    {
563                        return new string(' ', FractionFormat.Length);
564                    }
565                    else
566                    {
567                        return 0.ToString(fmt[0]) + "/" + 1.ToString(fmt[0]);
568                    }
569                }
570
571                int maxDigits = fmt[1].Length;
572                string sign = d < 0 ? "-" : "";
573                if (fixedDenominator == 0)
574                {
575                    List<double> numerators = new List<double>() { 1, 0 };
576                    List<double> denominators = new List<double>() { 0, 1 };
577
578                    if (maxDigits < 1 && maxDigits > 12)
579                    {
580                        throw (new ArgumentException("Number of digits out of range (1-12)"));
581                    }
582
583                    int maxNum = 0;
584                    for (int i = 0; i < maxDigits; i++)
585                    {
586                        maxNum += 9 * (int)(Math.Pow((double)10, (double)i));
587                    }
588
589                    double divRes = 1 / ((double)Math.Abs(d) - intPart);
590                    double result, prevResult = double.NaN;
591                    int listPos = 2, index = 1;
592                    while (true)
593                    {
594                        index++;
595                        double intDivRes = Math.Floor(divRes);
596                        numerators.Add((intDivRes * numerators[index - 1] + numerators[index - 2]));
597                        if (numerators[index] > maxNum)
598                        {
599                            break;
600                        }
601
602                        denominators.Add((intDivRes * denominators[index - 1] + denominators[index - 2]));
603
604                        result = numerators[index] / denominators[index];
605                        if (denominators[index] > maxNum)
606                        {
607                            break;
608                        }
609                        listPos = index;
610
611                        if (result == prevResult) break;
612
613                        if (result == d) break;
614
615                        prevResult = result;
616
617                        divRes = 1 / (divRes - intDivRes);  //Rest
618                    }
619                   
620                    numerator = (int)numerators[listPos];
621                    denomerator = (int)denominators[listPos];
622                }
623                else
624                {
625                    numerator = (int)Math.Round((d - intPart) / (1D / fixedDenominator), 0);
626                    denomerator = fixedDenominator;
627                }
628                if (numerator == denomerator || numerator==0)
629                {
630                    if(numerator == denomerator) intPart++;
631                    return sign + intPart.ToString(NetFormat).Replace("?", new string(' ', FractionFormat.Length));
632                }
633                else if (intPart == 0)
634                {
635                    return sign + FmtInt(numerator, fmt[0]) + "/" + FmtInt(denomerator, fmt[1]);
636                }
637                else
638                {
639                    return sign + intPart.ToString(NetFormat).Replace("?", FmtInt(numerator, fmt[0]) + "/" + FmtInt(denomerator, fmt[1]));
640                }
641            }
642
643            private string FmtInt(double value, string format)
644            {
645                string v = value.ToString("#");
646                string pad = "";
647                if (v.Length < format.Length)
648                {
649                    for (int i = format.Length - v.Length-1; i >= 0; i--)
650                    {
651                        if (format[i] == '?')
652                        {
653                            pad += " ";
654                        }
655                        else if (format[i] == ' ')
656                        {
657                            pad += "0";
658                        }
659                    }
660                }
661                return pad + v;
662            }
663        }
664        #endregion
665    }
666}
Note: See TracBrowser for help on using the repository browser.