/* ====================================================================
Licensed to the Apache Software Foundation (ASF) Under one or more
contributor license agreements. See the NOTICE file distributed with
this work for Additional information regarding copyright ownership.
The ASF licenses this file to You Under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed Under the License is distributed on an "AS Is" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations Under the License.
==================================================================== */
namespace HH.WMS.Utils.NPOI.SS.Formula
{
using System;
using System.Text;
using System.Text.RegularExpressions;
using HH.WMS.Utils.NPOI.Util;
using System.Globalization;
/**
* Formats sheet names for use in formula expressions.
*
* @author Josh Micich
*/
public class SheetNameFormatter
{
private static String BIFF8_LAST_COLUMN = "IV";
private static int BIFF8_LAST_COLUMN_TEXT_LEN = BIFF8_LAST_COLUMN.Length;
private static String BIFF8_LAST_ROW = (0x10000).ToString(CultureInfo.InvariantCulture);
private static int BIFF8_LAST_ROW_TEXT_LEN = BIFF8_LAST_ROW.Length;
private static char DELIMITER = '\'';
private static string CELL_REF_PATTERN = "^([A-Za-z]+)([0-9]+)$";
private SheetNameFormatter()
{
// no instances of this class
}
/**
* Used to format sheet names as they would appear in cell formula expressions.
* @return the sheet name UnChanged if there is no need for delimiting. Otherwise the sheet
* name is enclosed in single quotes ('). Any single quotes which were already present in the
* sheet name will be converted to double single quotes ('').
*/
public static String Format(String rawSheetName)
{
StringBuilder sb = new StringBuilder(rawSheetName.Length + 2);
AppendFormat(sb, rawSheetName);
return sb.ToString();
}
/**
* Convenience method for when a StringBuilder is already available
*
* @param out - sheet name will be Appended here possibly with delimiting quotes
*/
public static void AppendFormat(StringBuilder out1, String rawSheetName)
{
bool needsQuotes = NeedsDelimiting(rawSheetName);
if (needsQuotes)
{
out1.Append(DELIMITER);
AppendAndEscape(out1, rawSheetName);
out1.Append(DELIMITER);
}
else
{
out1.Append(rawSheetName);
}
}
public static void AppendFormat(StringBuilder out1, String workbookName, String rawSheetName)
{
bool needsQuotes = NeedsDelimiting(workbookName) || NeedsDelimiting(rawSheetName);
if (needsQuotes)
{
out1.Append(DELIMITER);
out1.Append('[');
AppendAndEscape(out1, workbookName.Replace('[', '(').Replace(']', ')'));
out1.Append(']');
AppendAndEscape(out1, rawSheetName);
out1.Append(DELIMITER);
}
else
{
out1.Append('[');
out1.Append(workbookName);
out1.Append(']');
out1.Append(rawSheetName);
}
}
private static void AppendAndEscape(StringBuilder sb, String rawSheetName)
{
int len = rawSheetName.Length;
for (int i = 0; i < len; i++)
{
char ch = rawSheetName[i];
if (ch == DELIMITER)
{
// single quotes (') are encoded as ('')
sb.Append(DELIMITER);
}
sb.Append(ch);
}
}
private static bool NeedsDelimiting(String rawSheetName)
{
int len = rawSheetName.Length;
if (len < 1)
{
throw new Exception("Zero Length string is an invalid sheet name");
}
if (Char.IsDigit(rawSheetName[0]))
{
// sheet name with digit in the first position always requires delimiting
return true;
}
for (int i = 0; i < len; i++)
{
char ch = rawSheetName[i];
if (IsSpecialChar(ch))
{
return true;
}
}
if (Char.IsLetter(rawSheetName[0])
&& Char.IsDigit(rawSheetName[len - 1]))
{
// note - values like "A$1:$C$20" don't Get this far
if (NameLooksLikePlainCellReference(rawSheetName))
{
return true;
}
}
if (NameLooksLikeBooleanLiteral(rawSheetName))
{
return true;
}
return false;
}
private static bool NameLooksLikeBooleanLiteral(String rawSheetName)
{
switch (rawSheetName[0])
{
case 'T':
case 't':
return "TRUE".Equals(rawSheetName, StringComparison.OrdinalIgnoreCase);
case 'F':
case 'f':
return "FALSE".Equals(rawSheetName, StringComparison.OrdinalIgnoreCase);
}
return false;
}
/**
* @return
Ptg
* tokens into human readable text form. In formula expressions, a sheet name always has a
* trailing '!' so there is little chance for ambiguity. It doesn't matter too much what this
* method returns but it is worth noting the likely consumers of these formula text strings:
* * * @return*
* Input Result Comments * "A1" true * "a111" true * "AA" false * "aa1" true * "A1A" false * "A1A1" false * "A$1:$C$20" false Not a plain cell reference * "SALES20080101" true *Still needs delimiting even though well out of range