jxl.biff.FormatRecord Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jxl Show documentation
Show all versions of jxl Show documentation
JExcelApi is a java library which provides the ability to read, write, and modify Microsoft Excel spreadsheets.
The newest version!
/*********************************************************************
*
* Copyright (C) 2002 Andrew Khan
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
***************************************************************************/
package jxl.biff;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import jxl.common.Logger;
import jxl.WorkbookSettings;
import jxl.format.Format;
import jxl.read.biff.Record;
/**
* A non-built in format record
*/
public class FormatRecord extends WritableRecordData
implements DisplayFormat, Format
{
/**
* The logger
*/
public static Logger logger = Logger.getLogger(FormatRecord.class);
/**
* Initialized flag
*/
private boolean initialized;
/**
* The raw data
*/
private byte[] data;
/**
* The index code
*/
private int indexCode;
/**
* The formatting string
*/
private String formatString;
/**
* Indicates whether this is a date formatting record
*/
private boolean date;
/**
* Indicates whether this a number formatting record
*/
private boolean number;
/**
* The format object
*/
private java.text.Format format;
/**
* The date strings to look for
*/
private static String[] dateStrings = new String[]
{
"dd",
"mm",
"yy",
"hh",
"ss",
"m/",
"/d"
};
// Type to distinguish between biff7 and biff8
private static class BiffType
{
}
public static final BiffType biff8 = new BiffType();
public static final BiffType biff7 = new BiffType();
/**
* Constructor invoked when copying sheets
*
* @param fmt the format string
* @param refno the index code
*/
FormatRecord(String fmt, int refno)
{
super(Type.FORMAT);
formatString = fmt;
indexCode = refno;
initialized = true;
}
/**
* Constructor used by writable formats
*/
protected FormatRecord()
{
super(Type.FORMAT);
initialized = false;
}
/**
* Copy constructor - can be invoked by public access
*
* @param fr the format to copy
*/
protected FormatRecord(FormatRecord fr)
{
super(Type.FORMAT);
initialized = false;
formatString = fr.formatString;
date = fr.date;
number = fr.number;
// format = (java.text.Format) fr.format.clone();
}
/**
* Constructs this object from the raw data. Used when reading in a
* format record
*
* @param t the raw data
* @param ws the workbook settings
* @param biffType biff type dummy overload
*/
public FormatRecord(Record t, WorkbookSettings ws, BiffType biffType)
{
super(t);
byte[] data = getRecord().getData();
indexCode = IntegerHelper.getInt(data[0], data[1]);
initialized = true;
if (biffType == biff8)
{
int numchars = IntegerHelper.getInt(data[2], data[3]);
if (data[4] == 0)
{
formatString = StringHelper.getString(data, numchars, 5, ws);
}
else
{
formatString = StringHelper.getUnicodeString(data, numchars, 5);
}
}
else
{
int numchars = data[2];
byte[] chars = new byte[numchars];
System.arraycopy(data, 3, chars, 0, chars.length);
formatString = new String(chars);
}
date = false;
number = false;
// First see if this is a date format
for (int i = 0 ; i < dateStrings.length; i++)
{
String dateString = dateStrings[i];
if (formatString.indexOf(dateString) != -1 ||
formatString.indexOf(dateString.toUpperCase()) != -1)
{
date = true;
break;
}
}
// See if this is number format - look for the # or 0 characters
if (!date)
{
if (formatString.indexOf('#') != -1 ||
formatString.indexOf('0') != -1 )
{
number = true;
}
}
}
/**
* Used to get the data when writing out the format record
*
* @return the raw data
*/
public byte[] getData()
{
data = new byte[formatString.length() * 2 + 3 + 2];
IntegerHelper.getTwoBytes(indexCode, data, 0);
IntegerHelper.getTwoBytes(formatString.length(), data, 2);
data[4] = (byte) 1; // unicode indicator
StringHelper.getUnicodeBytes(formatString, data, 5);
return data;
}
/**
* Gets the format index of this record
*
* @return the format index of this record
*/
public int getFormatIndex()
{
return indexCode;
}
/**
* Accessor to see whether this object is initialized or not.
*
* @return TRUE if this font record has been initialized, FALSE otherwise
*/
public boolean isInitialized()
{
return initialized;
}
/**
* Sets the index of this record. Called from the FormattingRecords
* object
*
* @param pos the position of this font in the workbooks font list
*/
public void initialize(int pos)
{
indexCode = pos;
initialized = true;
}
/**
* Replaces all instances of search with replace in the input. Used for
* replacing microsoft number formatting characters with java equivalents
*
* @param input the format string
* @param search the Excel character to be replaced
* @param replace the java equivalent
* @return the input string with the specified substring replaced
*/
protected final String replace(String input, String search, String replace)
{
String fmtstr = input;
int pos = fmtstr.indexOf(search);
while (pos != -1)
{
StringBuffer tmp = new StringBuffer(fmtstr.substring(0, pos));
tmp.append(replace);
tmp.append(fmtstr.substring(pos + search.length()));
fmtstr = tmp.toString();
pos = fmtstr.indexOf(search);
}
return fmtstr;
}
/**
* Called by the immediate subclass to set the string
* once the Java-Excel replacements have been done
*
* @param s the format string
*/
protected final void setFormatString(String s)
{
formatString = s;
}
/**
* Sees if this format is a date format
*
* @return TRUE if this format is a date
*/
public final boolean isDate()
{
return date;
}
/**
* Sees if this format is a number format
*
* @return TRUE if this format is a number
*/
public final boolean isNumber()
{
return number;
}
/**
* Gets the java equivalent number format for the formatString
*
* @return The java equivalent of the number format for this object
*/
public final NumberFormat getNumberFormat()
{
if (format != null && format instanceof NumberFormat)
{
return (NumberFormat) format;
}
try
{
String fs = formatString;
// Replace the Excel formatting characters with java equivalents
fs = replace(fs, "E+", "E");
fs = replace(fs, "_)", "");
fs = replace(fs, "_", "");
fs = replace(fs, "[Red]", "");
fs = replace(fs, "\\", "");
format = new DecimalFormat(fs);
}
catch (IllegalArgumentException e)
{
// Something went wrong with the date format - fail silently
// and return a default value
format = new DecimalFormat("#.###");
}
return (NumberFormat) format;
}
/**
* Gets the java equivalent date format for the formatString
*
* @return The java equivalent of the date format for this object
*/
public final DateFormat getDateFormat()
{
if (format != null && format instanceof DateFormat)
{
return (DateFormat) format;
}
String fmt = formatString;
// Replace the AM/PM indicator with an a
int pos = fmt.indexOf("AM/PM");
while (pos != -1)
{
StringBuffer sb = new StringBuffer(fmt.substring(0, pos));
sb.append('a');
sb.append(fmt.substring(pos + 5));
fmt = sb.toString();
pos = fmt.indexOf("AM/PM");
}
// Replace ss.0 with ss.SSS (necessary to always specify milliseconds
// because of NT)
pos = fmt.indexOf("ss.0");
while (pos != -1)
{
StringBuffer sb = new StringBuffer(fmt.substring(0, pos));
sb.append("ss.SSS");
// Keep going until we run out of zeros
pos += 4;
while (pos < fmt.length() && fmt.charAt(pos) == '0')
{
pos++;
}
sb.append(fmt.substring(pos));
fmt = sb.toString();
pos = fmt.indexOf("ss.0");
}
// Filter out the backslashes
StringBuffer sb = new StringBuffer();
for (int i = 0; i < fmt.length(); i++)
{
if (fmt.charAt(i) != '\\')
{
sb.append(fmt.charAt(i));
}
}
fmt = sb.toString();
// If the date format starts with anything inside square brackets then
// filter tham out
if (fmt.charAt(0) == '[')
{
int end = fmt.indexOf(']');
if (end != -1)
{
fmt = fmt.substring(end+1);
}
}
// Get rid of some spurious characters that can creep in
fmt = replace(fmt, ";@", "");
// We need to convert the month indicator m, to upper case when we
// are dealing with dates
char[] formatBytes = fmt.toCharArray();
for (int i = 0; i < formatBytes.length; i++)
{
if (formatBytes[i] == 'm')
{
// Firstly, see if the preceding character is also an m. If so,
// copy that
if (i > 0 && (formatBytes[i - 1] == 'm' || formatBytes[i - 1] == 'M'))
{
formatBytes[i] = formatBytes[i - 1];
}
else
{
// There is no easy way out. We have to deduce whether this an
// minute or a month? See which is closest out of the
// letters H d s or y
// First, h
int minuteDist = Integer.MAX_VALUE;
for (int j = i - 1; j > 0; j--)
{
if (formatBytes[j] == 'h')
{
minuteDist = i - j;
break;
}
}
for (int j = i + 1; j < formatBytes.length; j++)
{
if (formatBytes[j] == 'h')
{
minuteDist = Math.min(minuteDist, j - i);
break;
}
}
for (int j = i - 1; j > 0; j--)
{
if (formatBytes[j] == 'H')
{
minuteDist = i - j;
break;
}
}
for (int j = i + 1; j < formatBytes.length; j++)
{
if (formatBytes[j] == 'H')
{
minuteDist = Math.min(minuteDist, j - i);
break;
}
}
// Now repeat for s
for (int j = i - 1; j > 0; j--)
{
if (formatBytes[j] == 's')
{
minuteDist = Math.min(minuteDist, i - j);
break;
}
}
for (int j = i + 1; j < formatBytes.length; j++)
{
if (formatBytes[j] == 's')
{
minuteDist = Math.min(minuteDist, j - i);
break;
}
}
// We now have the distance of the closest character which could
// indicate the the m refers to a minute
// Repeat for d and y
int monthDist = Integer.MAX_VALUE;
for (int j = i - 1; j > 0; j--)
{
if (formatBytes[j] == 'd')
{
monthDist = i - j;
break;
}
}
for (int j = i + 1; j < formatBytes.length; j++)
{
if (formatBytes[j] == 'd')
{
monthDist = Math.min(monthDist, j - i);
break;
}
}
// Now repeat for y
for (int j = i - 1; j > 0; j--)
{
if (formatBytes[j] == 'y')
{
monthDist = Math.min(monthDist, i - j);
break;
}
}
for (int j = i + 1; j < formatBytes.length; j++)
{
if (formatBytes[j] == 'y')
{
monthDist = Math.min(monthDist, j - i);
break;
}
}
if (monthDist < minuteDist)
{
// The month indicator is closer, so convert to a capital M
formatBytes[i] = Character.toUpperCase(formatBytes[i]);
}
else if ((monthDist == minuteDist) &&
(monthDist != Integer.MAX_VALUE))
{
// They are equidistant. As a tie-breaker, take the formatting
// character which precedes the m
char ind = formatBytes[i - monthDist];
if (ind == 'y' || ind == 'd')
{
// The preceding item indicates a month measure, so convert
formatBytes[i] = Character.toUpperCase(formatBytes[i]);
}
}
}
}
}
try
{
this.format = new SimpleDateFormat(new String(formatBytes));
}
catch (IllegalArgumentException e)
{
// There was a spurious character - fail silently
this.format = new SimpleDateFormat("dd MM yyyy hh:mm:ss");
}
return (DateFormat) this.format;
}
/**
* Gets the index code, for use as a hash value
*
* @return the ifmt code for this cell
*/
public int getIndexCode()
{
return indexCode;
}
/**
* Gets the formatting string.
*
* @return the excel format string
*/
public String getFormatString()
{
return formatString;
}
/**
* Indicates whether this formula is a built in
*
* @return FALSE
*/
public boolean isBuiltIn()
{
return false;
}
/**
* Standard hash code method
* @return the hash code value for this object
*/
public int hashCode()
{
return formatString.hashCode();
}
/**
* Standard equals method. This compares the contents of two
* format records, and not their indexCodes, which are ignored
*
* @param o the object to compare
* @return TRUE if the two objects are equal, FALSE otherwise
*/
public boolean equals(Object o)
{
if (o == this)
{
return true;
}
if (!(o instanceof FormatRecord))
{
return false;
}
FormatRecord fr = (FormatRecord) o;
// Initialized format comparison
if (initialized && fr.initialized)
{
// Must be either a number or a date format
if (date != fr.date ||
number != fr.number)
{
return false;
}
return formatString.equals(fr.formatString);
}
// Uninitialized format comparison
return formatString.equals(fr.formatString);
}
}