Free cookie consent management tool by TermsFeed Policy Generator

source: branches/ExportSymbolicDataAnalysisSolutions/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/3.1.3/EPPlus-3.1.3/ExcelCellBase.cs @ 12214

Last change on this file since 12214 was 9580, checked in by sforsten, 12 years ago

#1730:

  • added SymbolicDataAnalysisExpressionExcelFormatter
  • changed modifiers in SymbolicExpressionTreeChart of methods SaveImageAsBitmap and SaveImageAsEmf to public
  • added menu item ExportSymbolicSolutionToExcelMenuItem to export a symbolic solution to an excel file
  • added EPPlus-3.1.3 to ExtLibs
File size: 27.0 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-27
31 *******************************************************************************/
32using System;
33using System.Collections.Generic;
34using System.Text;
35using OfficeOpenXml.Style;
36using System.Text.RegularExpressions;
37
38namespace OfficeOpenXml
39{
40    /// <summary>
41    /// Base class containing cell address manipulating methods.
42    /// </summary>
43    public abstract class ExcelCellBase
44    {
45        #region "public functions"
46        /// <summary>
47        /// Get the sheet, row and column from the CellID
48        /// </summary>
49        /// <param name="cellID"></param>
50        /// <param name="sheet"></param>
51        /// <param name="row"></param>
52        /// <param name="col"></param>
53        static internal void SplitCellID(ulong cellID, out int sheet, out int row, out int col)
54        {
55            sheet = (int)(cellID % 0x8000);
56            col = ((int)(cellID >> 15) & 0x3FF);
57            row = ((int)(cellID >> 29));
58        }
59        /// <summary>
60        /// Get the cellID for the cell.
61        /// </summary>
62        /// <param name="SheetID"></param>
63        /// <param name="row"></param>
64        /// <param name="col"></param>
65        /// <returns></returns>
66        internal static ulong GetCellID(int SheetID, int row, int col)
67        {
68            return ((ulong)SheetID) + (((ulong)col) << 15) + (((ulong)row) << 29);
69        }
70        #endregion
71        #region "Formula Functions"
72        private delegate string dlgTransl(string part, int row, int col, int rowIncr, int colIncr);
73        #region R1C1 Functions"
74        /// <summary>
75        /// Translates a R1C1 to an absolut address/Formula
76        /// </summary>
77        /// <param name="value">Address</param>
78        /// <param name="row">Current row</param>
79        /// <param name="col">Current column</param>
80        /// <returns>The RC address</returns>
81        public static string TranslateFromR1C1(string value, int row, int col)
82        {
83            return Translate(value, ToAbs, row, col, -1, -1);
84        }
85        /// <summary>
86        /// Translates a absolut address to R1C1 Format
87        /// </summary>
88        /// <param name="value">R1C1 Address</param>
89        /// <param name="row">Current row</param>
90        /// <param name="col">Current column</param>
91        /// <returns>The absolut address/Formula</returns>
92        public static string TranslateToR1C1(string value, int row, int col)
93        {
94            return Translate(value, ToR1C1, row, col, -1, -1);
95        }
96        /// <summary>
97        /// Translates betweein R1C1 or absolut addresses
98        /// </summary>
99        /// <param name="value">The addresss/function</param>
100        /// <param name="addressTranslator">The translating function</param>
101        /// <param name="row"></param>
102        /// <param name="col"></param>
103        /// <param name="rowIncr"></param>
104        /// <param name="colIncr"></param>
105        /// <returns></returns>
106        private static string Translate(string value, dlgTransl addressTranslator, int row, int col, int rowIncr, int colIncr)
107        {
108            if (value == "")
109                return "";
110            bool isText = false;
111            string ret = "";
112            string part = "";
113            char prevTQ = (char)0;
114            for (int pos = 0; pos < value.Length; pos++)
115            {
116                char c = value[pos];
117                if (c == '"' || c=='\'')
118                {
119                    if (isText == false && part != "" && prevTQ==c)
120                    {
121                        ret += addressTranslator(part, row, col, rowIncr, colIncr);
122                        part = "";
123                        prevTQ = (char)0;
124                    }
125                    prevTQ = c;
126                    isText = !isText;
127                    ret += c;
128                }
129                else if (isText)
130                {
131                    ret += c;
132                }
133                else
134                {
135                    if ((c == '-' || c == '+' || c == '*' || c == '/' ||
136                        c == '=' || c == '^' || c == ',' || c == ':' ||
137                        c == '<' || c == '>' || c == '(' || c == ')' || c == '!' ||
138                        c == ' ' || c == '&' || c == '%') &&
139                        (pos == 0 || value[pos - 1] != '[')) //Last part to allow for R1C1 style [-x]
140                    {
141                        ret += addressTranslator(part, row, col, rowIncr, colIncr) + c;
142                        part = "";
143                    }
144                    else
145                    {
146                        part += c;
147                    }
148                }
149            }
150            if (part != "")
151            {
152                ret += addressTranslator(part, row, col, rowIncr, colIncr);
153            }
154            return ret;
155        }
156        /// <summary>
157        /// Translate to R1C1
158        /// </summary>
159        /// <param name="part">the value to be translated</param>
160        /// <param name="row"></param>
161        /// <param name="col"></param>
162        /// <param name="rowIncr"></param>
163        /// <param name="colIncr"></param>
164        /// <returns></returns>
165        private static string ToR1C1(string part, int row, int col, int rowIncr, int colIncr)
166        {
167            int addrRow, addrCol;
168            string Ret = "R";
169            if (GetRowCol(part, out addrRow, out addrCol, false))
170            {
171                if (addrRow == 0 || addrCol == 0)
172                {
173                    return part;
174                }
175                if (part.IndexOf('$', 1) > 0)
176                {
177                    Ret += addrRow.ToString();
178                }
179                else if (addrRow - row != 0)
180                {
181                    Ret += string.Format("[{0}]", addrRow - row);
182                }
183
184                if (part.StartsWith("$"))
185                {
186                    return Ret + "C" + addrCol;
187                }
188                else if (addrCol - col != 0)
189                {
190                    return Ret + "C" + string.Format("[{0}]", addrCol - col);
191                }
192                else
193                {
194                    return Ret + "C";
195                }
196            }
197            else
198            {
199                return part;
200            }
201        }
202        /// <summary>
203        /// Translates to absolute address
204        /// </summary>
205        /// <param name="part"></param>
206        /// <param name="row"></param>
207        /// <param name="col"></param>
208        /// <param name="rowIncr"></param>
209        /// <param name="colIncr"></param>
210        /// <returns></returns>
211        private static string ToAbs(string part, int row, int col, int rowIncr, int colIncr)
212        {
213            string check = part.ToUpper();
214
215            int rStart = check.IndexOf("R");
216            if (rStart != 0)
217                return part;
218            if (part.Length == 1) //R
219            {
220                return GetAddress(row, col);
221            }
222
223            int cStart = check.IndexOf("C");
224            bool absoluteRow, absoluteCol;
225            if (cStart == -1)
226            {
227                int RNum = GetRC(part, row, out absoluteRow);
228                if (RNum > int.MinValue)
229                {
230                    return GetAddress(RNum, absoluteRow, col, false);
231                }
232                else
233                {
234                    return part;
235                }
236            }
237            else
238            {
239                int RNum = GetRC(part.Substring(1, cStart - 1), row, out absoluteRow);
240                int CNum = GetRC(part.Substring(cStart + 1, part.Length - cStart - 1), col, out absoluteCol);
241                if (RNum > int.MinValue && CNum > int.MinValue)
242                {
243                    return GetAddress(RNum, absoluteRow, CNum, absoluteCol);
244                }
245                else
246                {
247                    return part;
248                }
249            }
250        }
251        /// <summary>
252        /// Adds or subtracts a row or column to an address
253        /// </summary>
254        /// <param name="Address"></param>
255        /// <param name="row"></param>
256        /// <param name="col"></param>
257        /// <param name="rowIncr"></param>
258        /// <param name="colIncr"></param>
259        /// <returns></returns>
260        private static string AddToRowColumnTranslator(string Address, int row, int col, int rowIncr, int colIncr)
261        {
262            int fromRow, fromCol;
263            if (Address == "#REF!")
264            {
265                return Address;
266            }
267            if (GetRowCol(Address, out fromRow, out fromCol, false))
268            {
269                if (fromRow == 0 || fromCol == 0)
270                {
271                    return Address;
272                }
273                if (rowIncr != 0 && row != 0 && fromRow >= row && Address.IndexOf('$', 1) == -1)
274                {
275                    if (fromRow < row - rowIncr)
276                    {
277                        return "#REF!";
278                    }
279
280                    fromRow = fromRow + rowIncr;
281                }
282
283                if (colIncr != 0 && col != 0 && fromCol >= col && Address.StartsWith("$") == false)
284                {
285                    if (fromCol < col - colIncr)
286                    {
287                        return "#REF!";
288                    }
289
290                    fromCol = fromCol + colIncr;
291                }
292
293                Address = GetAddress(fromRow, Address.IndexOf('$', 1) > -1, fromCol, Address.StartsWith("$"));
294            }
295            return Address;
296        }
297
298        /// <summary>
299        /// Returns with brackets if the value is negative
300        /// </summary>
301        /// <param name="v">The value</param>
302        /// <returns></returns>
303        private static string GetRCFmt(int v)
304        {
305            return (v < 0 ? string.Format("[{0}]", v) : v > 0 ? v.ToString() : "");
306        }
307        /// <summary>
308        /// Get the offset value for RC format
309        /// </summary>
310        /// <param name="value"></param>
311        /// <param name="OffsetValue"></param>
312        /// <param name="fixedAddr"></param>
313        /// <returns></returns>
314        private static int GetRC(string value, int OffsetValue, out bool fixedAddr)
315        {
316            if (value == "")
317            {
318                fixedAddr = false;
319                return OffsetValue;
320            }
321            int num;
322            if (value[0] == '[' && value[value.Length - 1] == ']') //Offset?               
323            {
324                fixedAddr = false;
325                if (int.TryParse(value.Substring(1, value.Length - 2), out num))
326                {
327                    return (OffsetValue + num);
328                }
329                else
330                {
331                    return int.MinValue;
332                }
333            }
334            else
335            {
336                fixedAddr = true;
337                if (int.TryParse(value, out num))
338                {
339                    return num;
340                }
341                else
342                {
343                    return int.MinValue;
344                }
345            }
346        }
347        #endregion
348        #region "Address Functions"
349        #region GetColumnLetter
350        /// <summary>
351        /// Returns the character representation of the numbered column
352        /// </summary>
353        /// <param name="iColumnNumber">The number of the column</param>
354        /// <returns>The letter representing the column</returns>
355        protected internal static string GetColumnLetter(int iColumnNumber)
356        {
357
358            if (iColumnNumber < 1)
359            {
360                //throw new Exception("Column number is out of range");
361                return "#REF!";
362            }
363
364            string sCol = "";
365            do
366            {
367                sCol = ((char)('A' + ((iColumnNumber - 1) % 26))) + sCol;
368                iColumnNumber = (iColumnNumber - ((iColumnNumber - 1) % 26)) / 26;
369            }
370            while (iColumnNumber > 0);
371            return sCol;
372        }
373        #endregion
374        /// <summary>
375        /// Get the row/columns for a Cell-address
376        /// </summary>
377        /// <param name="CellAddress">The address</param>
378        /// <param name="FromRow">Returns the to column</param>
379        /// <param name="FromColumn">Returns the from column</param>
380        /// <param name="ToRow">Returns the to row</param>
381        /// <param name="ToColumn">Returns the from row</param>
382        internal static bool GetRowColFromAddress(string CellAddress, out int FromRow, out int FromColumn, out int ToRow, out int ToColumn)
383        {
384            bool ret;
385            CellAddress = CellAddress.ToUpper();
386            //This one can be removed when the worksheet Select format is fixed
387            if (CellAddress.IndexOf(' ') > 0)
388            {
389                CellAddress = CellAddress.Substring(0, CellAddress.IndexOf(' '));
390            }
391
392            if (CellAddress.IndexOf(':') < 0)
393            {
394                ret=GetRowColFromAddress(CellAddress, out FromRow, out FromColumn);
395                ToColumn = FromColumn;
396                ToRow = FromRow;
397            }
398            else
399            {
400                string[] cells = CellAddress.Split(':');
401                ret=GetRowColFromAddress(cells[0], out FromRow, out FromColumn);
402                if (ret)
403                    ret = GetRowColFromAddress(cells[1], out ToRow, out ToColumn);
404                else
405                {
406                    GetRowColFromAddress(cells[1], out ToRow, out ToColumn);
407                }
408
409                if (FromColumn <= 0)
410                    FromColumn = 1;
411                if (FromRow <= 0)
412                    FromRow = 1;
413                if (ToColumn <= 0)
414                    ToColumn = ExcelPackage.MaxColumns;
415                if (ToRow <= 0)
416                    ToRow = ExcelPackage.MaxRows;
417            }
418            return ret;
419        }
420        /// <summary>
421        /// Get the row/column for n Cell-address
422        /// </summary>
423        /// <param name="CellAddress">The address</param>
424        /// <param name="Row">Returns Tthe row</param>
425        /// <param name="Column">Returns the column</param>
426        /// <returns>true if valid</returns>
427        internal static bool GetRowColFromAddress(string CellAddress, out int Row, out int Column)
428        {
429            return GetRowCol(CellAddress, out Row, out Column, true);
430        }
431        /// <summary>
432        /// Get the row/column for a Cell-address
433        /// </summary>
434        /// <param name="address">the address</param>
435        /// <param name="row">returns the row</param>
436        /// <param name="col">returns the column</param>
437        /// <param name="throwException">throw exception if invalid, otherwise returns false</param>
438        /// <returns></returns>
439        internal static bool GetRowCol(string address, out int row, out int col, bool throwException)
440        {
441            bool colPart = true;
442            string sRow = "", sCol = "";
443            col = 0;
444            if (address.IndexOf(':') > 0)  //If it is a mult-cell address use
445            {
446                address = address.Substring(0, address.IndexOf(':'));
447            }
448            if (address.EndsWith("#REF!"))
449            {
450                row = 0;
451                col = 0;
452                return true;
453            }
454
455            int sheetMarkerIndex = address.IndexOf('!');
456            if (sheetMarkerIndex >= 0)
457            {
458                address = address.Substring(sheetMarkerIndex + 1);
459            }
460
461            for (int i = 0; i < address.Length; i++)
462            {
463                if ((address[i] >= 'A' && address[i] <= 'Z') && colPart && sCol.Length <= 3)
464                {
465                    sCol += address[i];
466                }
467                else if (address[i] >= '0' && address[i] <= '9')
468                {
469                    sRow += address[i];
470                    colPart = false;
471                }
472                else if (address[i] != '$') // $ is ignored here
473                {
474                    if (throwException)
475                    {
476                        throw (new Exception(string.Format("Invalid Address format {0}", address)));
477                    }
478                    else
479                    {
480                        row = 0;
481                        col = 0;
482                        return false;
483                    }
484                }
485            }
486
487            // Get the column number
488            if (sCol != "")
489            {
490                int len = sCol.Length - 1;
491                for (int i = len; i >= 0; i--)
492                {
493                    col += (((int)sCol[i]) - 64) * (int)(Math.Pow(26, len - i));
494                }
495            }
496            else
497            {
498                col = 0;
499                int.TryParse(sRow, out row);
500                return row>0;
501            }
502            // Get the row number
503            if (sRow == "") //Blank, fullRow
504            {
505                //if (throwException)
506                //{
507                //    throw (new Exception(string.Format("Invalid Address format {0}", address)));
508                //}
509                //else
510                //{                   
511                row = 0;
512                return col > 0;
513                //}
514            }
515            else
516            {
517                return int.TryParse(sRow, out row);
518            }
519        }
520        #region GetAddress
521        /// <summary>
522        /// Returns the AlphaNumeric representation that Excel expects for a Cell Address
523        /// </summary>
524        /// <param name="Row">The number of the row</param>
525        /// <param name="Column">The number of the column in the worksheet</param>
526        /// <returns>The cell address in the format A1</returns>
527        public static string GetAddress(int Row, int Column)
528        {
529            return GetAddress(Row, Column,false);
530        }
531        /// <summary>
532        /// Returns the AlphaNumeric representation that Excel expects for a Cell Address
533        /// </summary>
534        /// <param name="Row">The number of the row</param>
535        /// <param name="Column">The number of the column in the worksheet</param>
536        /// <param name="AbsoluteRow">Absolute row</param>
537        /// <param name="AbsoluteCol">Absolute column</param>
538        /// <returns>The cell address in the format A1</returns>
539        public static string GetAddress(int Row, bool AbsoluteRow, int Column, bool AbsoluteCol)
540        {
541            return ( AbsoluteCol ? "$" : "") + GetColumnLetter(Column) + ( AbsoluteRow ? "$" : "") + Row.ToString();
542        }
543        /// <summary>
544        /// Returns the AlphaNumeric representation that Excel expects for a Cell Address
545        /// </summary>
546        /// <param name="Row">The number of the row</param>
547        /// <param name="Column">The number of the column in the worksheet</param>
548        /// <param name="Absolute">Get an absolute address ($A$1)</param>
549        /// <returns>The cell address in the format A1</returns>
550        public static string GetAddress(int Row, int Column, bool Absolute)
551        {
552            if (Row == 0 || Column == 0)
553            {
554                return "#REF!";
555            }
556            if (Absolute)
557            {
558                return ("$" + GetColumnLetter(Column) + "$" + Row.ToString());
559            }
560            else
561            {
562                return (GetColumnLetter(Column) + Row.ToString());
563            }
564        }
565        /// <summary>
566        /// Returns the AlphaNumeric representation that Excel expects for a Cell Address
567        /// </summary>
568        /// <param name="FromRow">From row number</param>
569        /// <param name="FromColumn">From column number</param>
570        /// <param name="ToRow">To row number</param>
571        /// <param name="ToColumn">From column number</param>
572        /// <returns>The cell address in the format A1</returns>
573        public static string GetAddress(int FromRow, int FromColumn, int ToRow, int ToColumn)
574        {
575            return GetAddress(FromRow, FromColumn, ToRow, ToColumn, false);
576        }
577        /// <summary>
578        /// Returns the AlphaNumeric representation that Excel expects for a Cell Address
579        /// </summary>
580        /// <param name="FromRow">From row number</param>
581        /// <param name="FromColumn">From column number</param>
582        /// <param name="ToRow">To row number</param>
583        /// <param name="ToColumn">From column number</param>
584        /// <param name="Absolute">if true address is absolute (like $A$1)</param>
585        /// <returns>The cell address in the format A1</returns>
586        public static string GetAddress(int FromRow, int FromColumn, int ToRow, int ToColumn, bool Absolute)
587        {
588            if (FromRow == ToRow && FromColumn == ToColumn)
589            {
590                return GetAddress(FromRow, FromColumn, Absolute);
591            }
592            else
593            {
594                if (FromRow == 1 && ToRow == ExcelPackage.MaxRows)
595                {
596                    var absChar = Absolute ? "$" : "";
597                    return absChar + GetColumnLetter(FromColumn) + ":" + absChar + GetColumnLetter(ToColumn);
598                }
599                else if(FromColumn==1 && ToColumn==ExcelPackage.MaxColumns)
600                {
601                    var absChar = Absolute ? "$" : "";
602                    return absChar + FromRow.ToString() + ":" + absChar + ToRow.ToString();
603                }
604                else
605                {
606                    return GetAddress(FromRow, FromColumn, Absolute) + ":" + GetAddress(ToRow, ToColumn, Absolute);
607                }
608            }
609        }
610        /// <summary>
611        /// Get the full address including the worksheet name
612        /// </summary>
613        /// <param name="worksheetName">The name of the worksheet</param>
614        /// <param name="address">The address</param>
615        /// <returns>The full address</returns>
616        public static string GetFullAddress(string worksheetName, string address)
617        {
618            return GetFullAddress(worksheetName, address, true);
619        }
620        internal static string GetFullAddress(string worksheetName, string address, bool fullRowCol)
621        {
622               if (address.IndexOf("!") == -1 || address=="#REF!")
623               {
624                   if (fullRowCol)
625                   {
626                       string[] cells = address.Split(':');
627                       if (cells.Length > 0)
628                       {
629                           address = string.Format("'{0}'!{1}", worksheetName, cells[0]);
630                           if (cells.Length > 1)
631                           {
632                               address += string.Format(":{0}", cells[1]);
633                           }
634                       }
635                   }
636                   else
637                   {
638                       var a = new ExcelAddressBase(address);
639                       if ((a._fromRow == 1 && a._toRow == ExcelPackage.MaxRows) || (a._fromCol == 1 && a._toCol == ExcelPackage.MaxColumns))
640                       {
641                           address = string.Format("'{0}'!{1}{2}:{3}{4}", worksheetName, ExcelAddress.GetColumnLetter(a._fromCol), a._fromRow, ExcelAddress.GetColumnLetter(a._toCol), a._toRow);
642                       }
643                       else
644                       {
645                           address=GetFullAddress(worksheetName, address, true);
646                       }
647                   }
648               }
649               return address;
650        }
651        #endregion
652        #region IsValidCellAddress
653        /// <summary>
654        /// Checks that a cell address (e.g. A5) is valid.
655        /// </summary>
656        /// <param name="cellAddress">The alphanumeric cell address</param>
657        /// <returns>True if the cell address is valid</returns>
658        public static bool IsValidCellAddress(string cellAddress)
659        {
660            bool result = false;
661            try
662            {
663                int row, col;
664                if (GetRowColFromAddress(cellAddress, out row, out col))
665                {
666                    if (row > 0 && col > 0 && row <= ExcelPackage.MaxRows && col <= ExcelPackage.MaxColumns)
667                        result = true;
668                    else
669                        result = false;
670                }
671            }
672            catch { }
673            return result;
674        }
675        #endregion
676        #region UpdateFormulaReferences
677        /// <summary>
678        /// Updates the Excel formula so that all the cellAddresses are incremented by the row and column increments
679        /// if they fall after the afterRow and afterColumn.
680        /// Supports inserting rows and columns into existing templates.
681        /// </summary>
682        /// <param name="Formula">The Excel formula</param>
683        /// <param name="rowIncrement">The amount to increment the cell reference by</param>
684        /// <param name="colIncrement">The amount to increment the cell reference by</param>
685        /// <param name="afterRow">Only change rows after this row</param>
686        /// <param name="afterColumn">Only change columns after this column</param>
687        /// <returns></returns>
688        internal static string UpdateFormulaReferences(string Formula, int rowIncrement, int colIncrement, int afterRow, int afterColumn)
689        {
690            return Translate(Formula, AddToRowColumnTranslator, afterRow, afterColumn, rowIncrement, colIncrement);
691        }
692
693        #endregion
694        #endregion
695        #endregion
696    }
697}
Note: See TracBrowser for help on using the repository browser.