Format a Jtable Cell, if illegal format, then do nothing - java

Say I have following columns in my Jtable:
{"Item", "Price"}
I want format the price column to "0.00" format.
But if user enter an illegal string, then keep the original value of the cell.
ex, when the price is "1.23", but when user want to enter "abc", then the cell should still be "1.23"
How could I do this?
Thanks

Let's split your big problemn into smaller ones.
First, you want to have items of your Price column to be displayed according to a specified format, which you can achieve using a TableCellRenderer (which, when rendering numbers, will use a NumberFormat to display the correct number of decimals). As your table has only two columns of different types, the simplest solution is to use JTable#setDefaultRenderer(Class<?> columnClass, TableCellRenderer renderer). This way, your numbers will always be displayed using the correct number of decimals.
Second, for your edition issue, the same solution can be rather elegant : call JTable#setDefaultEditor(java.lang.Class, javax.swing.table.TableCellEditor) and set as editor a component one that will, before commiting change to your table model, ensure the new value is a valid number (valid of course according to your rules).

A JTable supports this by default. The table will choose the renderer/editor based on the data stored in each column. So all you need to do is override the getColumnClass(...) method to return Double.class. Then when you edit a cell the editor will make sure the value entered is a valid number.
If you want more control over how the number is formatted then you can use Table Format Renderer.

JFormattedTextField works the way you describe so just create a new CellEditor that extends JFormattedTextField.

Put the formatting command into a try/catch block. If the operation fails with an NumberFormatException, cancel the edit.
Using your original code, make a few minor changes:
public void setValue(Object value) {
NumberFormat formatter = new DecimalFormat("0.00");
String s = null;
try {
s = formatter.format(Integer.parseInt((String)value));
if (null != s) setText(s);
} catch (NumberFormatException ex) {
// what could I do here?
// You could display a warning JOptionPane and/or output to System.out
}
}

Related

JFormattedTextField and getValue vs. getText

After reading the docs it is not clear to me the difference between getValue and getText for a JFormattedTextField.
In my code, getText gives me what I think I need while getValue always returns null.
It seems to me, based on the docs, that they should both return the same thing at least after the field, when correctly formatted, loses focus.
The getValue method is supposed to "Returns the last valid value."
A simple explanation would be helpful.
Well JFormattedTextField is a text component that allows to keep a value and give it a custom String representation (format).
This value is an Object which is typically a Date or Number instance, the two classes with most different formats.
So having said this, getValue() returns the value held by formatted text field component while getText() returns the value's String representation.
For further details on this component please take a look to How to Use Formatted Text Fields:
A formatted text field's text and its value are two different
properties, and the value often lags behind the text.
The text property is defined by the JTextField class. This property
always reflects what the field displays. The value property, defined
by the JFormattedTextField class, might not reflect the latest text
displayed in the field. While the user is typing, the text property
changes, but the value property does not change until the changes are
committed.
To be more precise, the value of a formatted text field can be set by
using either the setValue method or the commitEdit method. The
setValue method sets the value to the specified argument. The argument
can technically be any Object, but the formatter needs to be able to
convert it into a string. Otherwise, the text field does not display
any substantive information.
Say you have
JFormattedTextField text = new JFormattedTextField(new DecimalFormat("####.##"));
This is a simple JFormatedTextField for numbers. You enter 12 (a valid entry), and both values will be the same. If you enter "Hello", this is invalid and will not be returned by getValue().
You have probably entered invalid data into the textfield.

javafx TableView left justify single column

I've got a tableview of floats which work fine. I've made it so the cells are editable, formatted for currency etc. All the cells are right-justified as one would expect a numerical field to be.
One of the columns references a string (instead of a float), which is also editable-- that works too, using the TextFieldTableCell.forTableColumn() method.
However I can't figure out how to left-justify the string -- it is right justified like the other columns. I tried using the .setAlignment(Pos.CENTER_LEFT) method, but it only left-justifies when the cell is being edited. After editing, it is right-justified again...
Here's the snippet for that particular column:
cargoTableTypeCol.setCellFactory((TableColumn<CargoItem, String> p) -> {
TableCell<CargoItem, String> cell = new TableCell<>();
cell.setAlignment(Pos.CENTER_LEFT);
return cell;
});
cargoTableTypeCol.setCellFactory(TextFieldTableCell.forTableColumn());
cargoTableTypeCol.setCellValueFactory(cellData -> cellData.getValue().typeProperty());
And here's what it looks like:
as you can see the cell with "coal" in it is not left-justified.
Iknow it's trivial, but frustrating nonetheless.
I suggest you to try the following: cargoTableTypeCol.setStyle("-fx-alignment: CENTER-LEFT;");
Using CSS, as in #UgurcanYildirim's answer (or using an external style sheet), is the best solution for this particular use case.
The reason the code in the question does not work is that the cellFactory is a property and follows the usual rules for properties. In particular, if you call setCellFactory(...) with one value and subsequently call it with a different value, the second value replaces the first, and the first is discarded. In other words, a TableColumn has one and only one cellFactory.
For a use case where you genuinely need to modify the cell returned by one cell factory, you can use the following (essentially just a decorator pattern):
Callback<TableColumn<CargoItem, String>, TableCell<CargoItem, String>> defaultCellFactory
= TextFieldTableCell.forTableColumn();
cargoTableTypeCol.setCellFactory(col -> {
TableCell<CargoItem, String> cell = defaultCellFactory.call(col);
cell.setAlignment(Pos.CENTER_LEFT);
return cell ;
});

apache poi get default column type

I'm using POI to create a new row of cells in an existing spreadsheet. POI allows you to get the column default style, but there's no equivalent (as near as I can tell) to getting a default type. I'm getting a String from my user interface and I don't know how to set the cell type. If the string is a double, then fine, it's NUMERIC. But if the String specifies a date, how would I best detect it so that it is also set to NUMERIC? There are some many formatting types for a date that it is impractical to detect the type from the cell style format. Does POI support a way to parse based on a format?
To set the cell type you use:
setCellType()
as outlined in the docs:
https://poi.apache.org/apidocs/
setCellType void setCellType(int cellType) Set the cells type (numeric, formula or string). If the cell currently contains a value,
the value will be converted to match the new type, if possible.
Formatting is generally lost in the process however.
If what you want to do is get a String value for your numeric cell,
stop!. This is not the way to do it. Instead, for fetching the string
value of a numeric or boolean or date cell, use DataFormatter instead.
Throws: java.lang.IllegalArgumentException - if the specified cell
type is invalid java.lang.IllegalStateException - if the current value
cannot be converted to the new type See Also: CELL_TYPE_NUMERIC,
CELL_TYPE_STRING, CELL_TYPE_FORMULA, CELL_TYPE_BLANK,
CELL_TYPE_BOOLEAN, CELL_TYPE_ERROR

Sqlite convert text to decimal?

I have an Sqlitedb, with a column: amount text not null
In column amount, I store dollar values in this format: $1.01, $40.58, etc.
I'm trying to sum up the dollar values using
SELECT SUM(amount) FROM record WHERE date LIKE '"+date+"'"
However, it doesn't work, presumably because you can't add text. I know this isn't the ideal storage but how would I go about parsing the amounts so that I can add them up?
Well, as you have stated this is far from the ideal way to store the values (which should be in decimal / real format from the start), however you could do something like this:
SELECT SUM(SUBSTR(amount, 2)) FROM record WHERE date LIKE '"+date+"'"
The SUBSTR method is a text function that will take a section of the text (in this case, from a starting index). The problem here was the $ sign, which was confused the dynamic type conversion into believing you were trying to add strings together.
This should now work as SQLLite allows for dynamic typing (which in a nutshell, means that the value is used for comparison, not the container).
It's much better to store the amounts as unscaled integers, and just format them when needed for display. You get more compact storage, and easier and faster addition. Using integers avoids the risk of rounding errors in floating-point representations.
By unscaled, I mean $1.23 would be stored as the integer 123. (Use long in your Java code, and INTEGER in the SQL.)
You can format amounts for display by querying the locale for the relevant info:
// implement this any way you like!
long amount = getAmountFromDb();
// use default locale if you want
Locale locale = getLocaleFromSomewhere();
Currency currency = Currency.getInstance(locale);
NumberFormat format = NumberFormat.getCurrencyInstance(locale);
int scale = currency.getDefaultFractionDigits();
BigDecimal scaledAmount = new BigDecimal(unscaledBalance).movePointLeft(scale);
String textAmount = format.format(scaledAmount);

Reading string value from Excel with HSSF but it's double

I'm using HSSF-POI for reading excel data. The problem is I have values in a cell that look like a number but really are strings. If I look at the format cell in Excel, it says the type is "text". Still the HSSF Cell thinks it's numeric. How can I get the value as a string?
If I try to use cell.getRichStringValue, I get exception; if cell.toString, it's not the exact same value as in Excel sheet.
Edit: until this gets resolved, I'll use
new BigDecimal(cell.getNumericCellValue()).toString()
The class you're looking for in POI is DataFormatter
When Excel writes the file, some cells are stored as literal Strings, while others are stored as numbers. For the latter, a floating point value representing the cell is stored in the file, so when you ask POI for the value of the cell that's what it actually has.
Sometimes though, especially when doing Text Extraction (but not always), you want to make the cell value look like it does in Excel. It isn't always possible to get that exactly in a String (non full space padding for example), but the DataFormatter class will get you close.
If you're after a String of the cell, looking much as you had it looking in Excel, just do:
// Create a formatter, do this once
DataFormatter formatter = new DataFormatter(Locale.US);
.....
for(Cell cell : row) {
CellReference ref = new CellReference(cell);
// eg "The value of B12 is 12.4%"
System.out.println("The value of " + ref.formatAsString() + " is " + formatter.formatCellValue(cell));
}
The formatter will return String cells as-is, and for Numeric cells will apply the formatting rules on the style to the number of the cell
If the documents you are parsing are always in a specific layout, you can change the cell type to "string" on the fly and then retrieve the value. For example, if column 2 should always be string data, set its cell type to string and then read it with the string-type get methods.
cell.setCellType(Cell.CELL_TYPE_STRING);
In my testing, changing the cell type did not modify the contents of the cell, but did allow it to be retrieved with either of the following approaches:
cell.getStringCellValue();
cell.getRichStringCellValue().getString();
Without an example of a value that is not converting properly, it is difficult to know if this will behave any differently than the cell.toString() approach you described in the description.
You mean HSSF-POI says
cell.getCellType() == Cell.CELL_TYPE_NUMERIC
NOT
Cell.CELL_TYPE_STRING as it should be?
I would think it's a bug in POI, but every cell contains a Variant, and Variant has a type. It's kind of hard to make a bug there, so instead I think Excel uses some extra data or heuristic to report the field as text. Usual MS way, alas.
P.S. You cannot use any getString() on a Variant containing numeric, as the binary representation of the Variant data depends on it's type, and trying to get a string from what is actually a number would result in garbage -- hence the exception.
This below code works fine to read any celltype but that cell should contain numeric value
new BigDecimal(cell.getNumericCellValue()));
e.g.
ase.setGss(new BigDecimal(hssfRow.getCell(3).getNumericCellValue()));
where variable gss is of BigDecimal type.
Excel will convert anything that looks like a number or date or time from a string. See MS Knowledge base article, which basically suggests to enter the number with an extra character that makes it a string.
You are probably dealing with an Excel problem. When you create the spreadsheet, the default cell type is Generic. With this type, Excel guesses the type based on the input and this type is saved with each cell.
When you later change the cell format to Text, you are just changing the default. Excel doesn't change every cell's type automatically. I haven't found a way to do this automatically.
To confirm this, you can go to Excel and retype one of the numbers and see if it's text in HSSF.
You can also look at the real cell type by using this function,
#Cell("type", A1)
A1 is the cell for the number. It shows "l" for text, "v" for numbers.
The problem with Excel is that the default format is generic. With this format Excel stores numbers entered in the cell as numeric. You have to change the format to text before entering the values. Reentering the values after changing the format will also work.
That will lead to little green triangles in the left upper corner of the cells if the content looks like a number to Excel. If this is the case the value is really stored as text.
With new BigDecimal(cell.getNumericCellValue()).toString() you will still have a lot of problems. For example if you have identifying numbers (e.g. part numbers or classification numbers) you probably have cases that have leading zeros which will be a problem with the getNumericCellValue() approach.
I try to thoroughly explain how to correctly create the Excel to the party creating the files I have to handle with POI. If the files are uploaded by end users I even have created a validation program to check for expected cell types if I know the columns in advance. As a by-product you can also check various other things of the supplied files (e.g. are the right columns provided or mandatory values).
"The problem is I have values in a cell that look like a number" => look like number when viewed in Excel?
"but really are strings" => what does that mean? How do you KNOW that they really are strings?
"If I look at the format cell" => what's "the format cell"???
'... in Excel, it says the type is "text"' => Please explain.
"Still the HSSF Cell thinks it's numeric." => do you mean that the_cell.getCellType() returns Cell.CELL_TYPE_NUMERIC?
"How can I get the value as a string?" => if it's NUMERIC, get the numeric value using the_cell.getNumericCellValue(), then format it as a string any way you want to.
"If I try to use cell.getRichStringValue, I get exception;" => so it's not a string.
"if cell.toString, it's not the exact same value as in Excel sheet." => so cell.toString() doesn't format it the way that Excel formats it.
Whatever heuristic Excel uses to determine type is irrelevant to you. It's the RESULT of that decision as stored in the file and revealed by getCellType() that matters.

Categories