Java8 Streams: Remove an field from an object of the map value - java-8

I have a hash map like this
Map<String, AttributeValueUpdate> myMap = new HashMap<>;
The class AttributeValueUpdate looks like this:
public class AttributeValueUpdate {
private AttributeValue value;
private String action;
public static class Builder {
private AttributeValue value;
private String action;
public Builder() {
}
public AttributeValueUpdate.Builder withValue(AttributeValue value) {
this.value = value;
return this;
}
public AttributeValueUpdate.Builder withAction(String action) {
this.action = action;
return this;
}
protected void populate(AttributeValueUpdate instance) {
instance.setValue(this.value);
instance.setAction(this.action);
}
public AttributeValueUpdate build() {
AttributeValueUpdate instance = new AttributeValueUpdate();
this.populate(instance);
return instance;
}
}
}
The map has two entries
AttributeValueUpdate att1 = AttributeValueUpdate.builder().withAction("Add").withValue(new AttributeValue("sam").build();
AttributeValueUpdate att2 = AttributeValueUpdate.builder().withAction("Delete").withValue(new AttributeValue("john").build();
myMap.add("entry1", attr1);
myMap.add("entry2", atte2);
I want to modify mymap by deleting the "value field" from all the AttributeValueUpdate (which is value of the map), basically map's value field will be changed by removing "value field" of the AttributeValueUpdate object. How can I achieve this using java streams?

Java Stream API is not a friend with Map as long as it's collection-based (List, Set). You need to stream over the entries of the map.
As far as I understand, you want to remove (= make null) AttributeValue value of each AttributeValueUpdate instance (map's value). Here is the way to go assuming a constructor AttributeValueUpdate(String action):
Map<String, AttributeValueUpdate> updatedMap = myMap.entrySet().stream()
.map(entry -> {
String action = entry.getValue().getAction();
AttributeValueUpdate update = new AttributeValueUpdate(action);
return new SimpleEntry<>(entry.getKey(), update);
})
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
The easiest solution is using Map#replaceAll if you don't mind to mutate the map:
myMap.replaceAll((k, v) -> {
String action = v.getAction();
return new AttributeValueUpdate(action);
});

Related

Collecting data from map to list using Java 8

Please help me with the Java 8 map - filter - sort - collect code.
Info.java
public class Info {
private final String name;
private final String desc;
private String version = null;
#Override
public boolean equals(Object that) {
if (that == null) {
return false;
}
if (that instanceof Info) {
Info other = (Info) that;
return Objects.equals(this.name, other.name) &&
Objects.equals(this.desc, other.desc) &&
Objects.equals(this.version, other.version);
} else {
return false;
}
}
public boolean equalsWithoutVersion(Object that) {
if (that == null) {
return false;
}
if (that instanceof Info) {
Info other = (Info) that;
return Objects.equals(this.name, other.name) &&
Objects.equals(this.desc, other.desc);
} else {
return false;
}
}
#Override
public int hashCode() {
int hash = 13;
hash = (hash * 7) + name.hashCode();
hash = (hash * 7) + desc.hashCode();
if (version != null)
hash = (hash * 7) + version.hashCode();
return hash;
}
#Override
public String toString() {
String versionString = version == null ? "latest" : version;
return String.format("Name: %s Desc: %s Key Type: %s Version: %s", this.name, this.desc, this.keyType.name(), versionString);
}
}
Value.java
public class Value implements Comparable<Value> {
private String data;
private String version;
public Value(String version, String data) {
this.version = version;
this.data = data;
}
#Override
public int compareTo(Value o) {
return (Integer.parseInt(this.version) > Integer.parseInt(o.version)) ? -1
: (Integer.parseInt(this.version) < Integer.parseInt(o.version)) ? 1
: 0;
}
}
Cache.java
public class Cache {
private final Map<Info, Value> dataMap = new HashMap<>();
...
private Value getlatestVersionFromCache(Info info) {
List<Value> values = dataMap.entrySet().stream()
.filter(p -> p.getKey().equalsWithoutVersion(info))
.sorted(Map.Entry.comparingByValue())
.map(x::getValue))
.collect(Collectors.toList());
return values.isEmpty() ? null : values.get(0);
}
}
The goal is to obtain the latest version of a record from the map. So first I am filtering entries from the map by comparing fields of Info without version. Then I am sorting the map by value based on version. Then I am collecting the values to a list. Then I can get the first element to get the latest version.
But I am getting the following compilation error on the filter statement:
Syntax error on token ")", ElidedSemicolonAndRightBrace expected
Well I doubt about your solution. I think you can do it in simple way. so first change version type to Integer in Value class(in compareTo() method you converted it to Integer). and also change method signature to Optional<Value> in getlatestVersionFromCache() method.
also I think you don't need to sort dataMap.
private Optional<Value> getlatestVersionFromCache(Info info) {
Map<Value,Integer> result = dataMap.entrySet()
.stream()
.filter(p -> p.getKey().equalsWithoutVersion(info))
.collect(Collectors.toMap(Map.Entry::getValue, entry -> entry.getValue().getVersion(), Integer::min));
Optional<Value> latestValue = result.keySet()
.stream()
.min(Comparator.comparingInt(key -> key.getVersion()));
return latestValue;
}
Better solution is something like this:
dataMap.entrySet()
.stream()
.filter(p -> p.getKey().equalsWithoutVersion(info))
.min(Comparator.comparingInt(entry -> entry.getValue().getVersion()))
.map(Map.Entry::getValue)
.orElse(null);
You have several omissions and errors in the code you posted, but the filter statement was actually fine.
The following passes compilation:
List<Value> values = dataMap.entrySet()
.stream()
.filter(p -> p.getKey().equalsWithoutVersion(info))
.sorted(Map.Entry.comparingByValue())
.map(Map.Entry::getValue) // was .map(x::getValue)) - x is not defined anywhere, so
// I assumed you meant Map.Entry::getValue
.collect(Collectors.toList());
class Info {
public static final BiPredicate<Info, Info> IS_EQUAL_IGNORE_VERSION =
(one, two) -> one.getName().equals(two.getName()) && one.getDesc().equals(two.getDesc());
private final String name;
private final String desc;
private String version;
}
private final Map<Info, Value> dataMap = new HashMap<>();
public Value getlatestVersionFromCache(Info info) {
Value value = null;
for (Map.Entry<Info, Value> entry : dataMap.entrySet())
if (Info.IS_EQUAL_IGNORE_VERSION.test(entry.getKey(), info))
if (value == null || Integer.parseInt(entry.getValue().getVersion()) > Integer.parseInt(value.getVersion()))
value = entry.getValue();
return value;
}
Notes:
I think that use a comparator to check that two objects are queal is not correct. You can use e.g. Predicate. And define it as a static method in the target class.
Pay attention on version. It seems that it should be an integer instead of String.
In your approach, compare two string is not correct, because it is used instead of equals method.
I think that using Java8 streams only because this is a stream is alow not correct.

Dynamic Filtering of ArrayList with another ArrayList Java 8

I have a few Search Results Objects as given below:
public class TradeSearchResult{
private String tradeRefNo;
private String relatedTradeId;
private String custodyDate;
private String orderNumber;
private String odrQty;
private String price : 500;
}
public class CollateralTradesSearchResult{
private String excludeTradeOUT;
private String settlementStatus;
private String fundId;
private String altFundId;
private String apNumber;
private String collateralOrderNumber;
private String componenetIdentifier;
}
Now I have a Search Filter Criteria object
public class CRITERION {
protected String field; //The field denotes any field name of the either
SearchResult object
protected String operator; its will be EQUALS or NOT_EQUALS
protected String value; Value of the field.
}
Now I need to write a Dynamic Filter Method where I will pass the List of Criterion object and can pass List of either SearchResult like below
public static List<Object> applyFilter(List<CRITERION> comp, List<?> objectList){
//The CRITERION.fiedName can be same in more than one in the list
return filteredList;
}
here is one example: Consider the below List of
TradeSearchResult{
tradeRefNo : W12343;
relatedTradeId: N993093;
custodyDate : 2018-12-14;
orderNumber : 0000342343;
String odrQty : 12;
String price : 500;
},
{
tradeRefNo : W12344;
relatedTradeId: N993093;
custodyDate : 2018-12-14;
orderNumber : 0000342344;
String odrQty : 18;
String price : 600;
},
{
tradeRefNo : W12345;
relatedTradeId: N993094;
custodyDate : 2018-12-14;
orderNumber : 0000342345;
String odrQty : 20;
String price : 700;
}
Now the Criterion class is like
CRITERION{
field :relatedTradeId;
operator : EQUALS;
value :N993093;
}
{
field :relatedTradeId;
operator : EQUALS;
value :N993094;
}
{
field :orderNumber ;
operator : EQUALS;
value :0000342344;
}
It will only return one result even relatedTradeId has two filter
TradeSearchResult{
tradeRefNo : W12344;
relatedTradeId: N993093;
custodyDate : 2018-12-14;
orderNumber : 0000342344;
String odrQty : 18;
String price : 600;
}
Now in the same applyFIlter Method I can send a list of Criterion and a list of CollateralTradesSearchResult and returns filtered result.
Here is something I tried
public static List<Object> applyFilter(List<CRITERION> criList, List<?> objectList){
long startTime = Calendar.getInstance().getTimeInMillis();
Set<Object> objectSet = new HashSet<>();
for(CRITERION cri : criList){
String fieldName = cri.getFIELD();
objectList.stream().filter(p->beanProperties(p).get(fieldName).equals(cri.getVALUE())).forEachOrdered(objectSet::add);
//objectList.retainAll(objectSet);
//objectSet.clear();
}
List<Object> ret = new ArrayList<>(objectSet);
long endTime = Calendar.getInstance().getTimeInMillis();
System.out.println("Size"+ ret.size());
System.out.println("Time Taken to Search"+ String.valueOf(endTime-startTime));
return ret;
}
Hereis the beanProperties() method
public static Map<String, Object> beanProperties(Object bean) {
try {
Map<String, Object> map = new HashMap<>();
Arrays.asList(Introspector.getBeanInfo(bean.getClass(), Object.class)
.getPropertyDescriptors())
.stream()
// filter out properties with setters only
.filter(pd -> Objects.nonNull(pd.getReadMethod()))
.forEach(pd -> { // invoke method to get value
try {
Object value = pd.getReadMethod().invoke(bean);
if (value != null) {
map.put(pd.getName(), value);
}
} catch (Exception e) {
// add proper error handling here
}
});
return map;
} catch (IntrospectionException e) {
// and here, too
return Collections.emptyMap();
}
}
Any help using Stream or by any means will be helpful.
Create an interface SearchResult and make both your classes implement it. Then create this class:
public class Filter<T extends SearchResult> {
public List<T> applyFilter(List<Criterion> criteria, List<T> list) {
Map<String, Set<String>> allowedValues = new HashMap<>();
Map<String, Set<String>> prohibitedValues = new HashMap<>();
populateValues(criteria, "EQUALS", allowedValues);
populateValues(criteria, "NOT_EQUALS", prohibitedValues);
prohibitedValues.forEach((k, v) -> list.removeIf(t -> v.contains(getFieldValue(k, t))));
allowedValues.forEach((k, v) -> list.removeIf(t -> !v.contains(getFieldValue(k, t))));
return list;
}
private static void populateValues(List<Criterion> criteria, String operator, Map<String, Set<String>> values) {
criteria.stream()
.filter(c -> c.getOperator().equals(operator))
.forEach(c -> {
values.merge(c.getField(), Stream.of(c.getValue()).collect(Collectors.toSet()),
(set1, set2) -> Stream.concat(set1.stream(), set2.stream()).collect(Collectors.toSet()));
});
}
private String getFieldValue(String fieldName, T object) {
Field field;
try {
field = object.getClass().getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
e.printStackTrace();
return null;
}
field.setAccessible(true);
try {
return (String) field.get(object);
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
}
And use it as:
Filter<TradeSearchResult> filter = new Filter<>(); //or CollateralTradesSearchResult
List<TradeSearchResult> filteredList = filter.applyFilter(criteria, searchResults);

I want to find element present in list and it compare with Enum value

I have a List it has multiple object is present. for every object we have to find some string which compare to Enum value How we get list of particular object
public enum ObjectType {
CONTACT("Contact"),
BANK_DETAILS("Bank-Details"),
EMPLOYMENT("Employment"),
PRODUCT("Product"),
INCOME_DETAIL("Income-Details");
ObjectType(String values) { this.values = values; }
public String getValues() { return values; }
private String values; }
for this i am using below method
List listOutput = errorsList.stream().
filter(e -> e.getObjType().contains(String.valueOf(ObjectType.values())))
.collect(Collectors.toList());
But it return 0 value are present
Try this (adding check method to enum):
public enum ObjectType {
CONTACT("Contact"),
BANK_DETAILS("Bank-Details"),
EMPLOYMENT("Employment"),
PRODUCT("Product"),
INCOME_DETAIL("Income-Details");
private String values;
ObjectType(String values) {
this.values = values;
}
public String getValues() {
return values;
}
public static boolean isValueExists(String test) {
for (ObjectType o : ObjectType.values()) {
if (o.getValues().equals(test)) {
return true;
}
}
return false;
}
}
And then use it:
List listOutput = errorsList.stream().filter(e -> ObjectType.isValueExists(e.getObjType())).collect(Collectors.toList());

wicket manipulating DefaultDataTable

I was creating a web application in wicket and had created a table which shows the user some information. Now I wanted to manipulate this table so if the cell contained "N" the background color would be RED and if it contained "Y" the background color would be GREEN. At the moment I was having trouble to determine what is actually inside the cell. I create my table by the following:
dataTable = new DefaultDataTable<TableModalInt, String>("table", columns,
new TableModalProvider(), 100000){
#SuppressWarnings("rawtypes")
#Override
protected Item newCellItem(String id, int index, IModel model) {
Item item = super.newCellItem(id, index, model);
if (id == "3"){
item.add(AttributeModifier.replace("align", "center"));
}
return item;
}
};
I am capable of determining the cell which I wanna now check what is being displayed to the user. Any help on how i can do this? to change the color i know I'll have to add item.add(AttributeModifier.replace("bgcolor", "red")); but don't know how to tell whats inside the cell
You should do your checks in the IColumn implementation.
https://github.com/apache/wicket/blob/24e9db6c8af85043ce36e4d25a0e8a2d8dc2f49e/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/data/table/PropertyColumn.java#L94 populates the Item with a Label. You need to add a AttributeModifier to the Label.
You can also achieve your goal with pure JavaScript and/or CSS at the client side.
Item is extending list item, so you can try .getModelObject and validate it if it is "X" or "Y"
http://wicket.apache.org/apidocs/1.5/org/apache/wicket/markup/repeater/Item.html
http://wicket.apache.org/apidocs/1.5/org/apache/wicket/markup/html/list/ListItem.html#getModelObject()
This example extracts the cell value when a cell is clicked in a Wicket DataView. The model for this DataView is a Map with String keys and Integer values: Map<String,Integer>.
The PropertyColumn list is created using with column headers ("ALPHA", "BETA", "GAMMA") and property expressions: "alpha", "beta", "gamma". PropertyColumn uses the expressions to retrieve the values from the map.
The DataView is created with the list of PropertyColumns and a DataProvider. DataView uses the DataProvider to populate the PropertyColumn when the table is rendered and reacts to clicks to expose the cell values.
Cells are exposed by overriding the newCellItem(String,int,IModel) method and calling the super-class method to get the cell. This example adds a behavior to react to "onclick" events. Within the event, the cell's first child component should be the Label used to display the cell value.
The innermost model of the cell Label is the PropertyModel from the PropertyColumn.
innerModel.getPropertyExpression(): Our data map key (String).
innerModel.getObject(): The data value (Integer).
innerModel.getInnermostModelOrObject(): The list item (Map<String,Integer>).
Wicket DataView: Extract Cell Value
public class MessageLogStatus
extends WebPage
{
/** Represents serialVersionUID. */
private static final long serialVersionUID = 20150701L;
private static final Logger log = LoggerFactory.getLogger(MessageLogStatus.class);
static final String A = "alpha";
static final String B = "beta";
static final String C = "gamma";
public MessageLogStatus()
{
super();
final List<String> keys = Arrays.asList(A, B, C);
final List<Map<String,Integer>> data = Arrays.asList
(
map(A, 1).put(B, 11).put(C, 21).toMap(),
map(A, 2).put(B, 12).put(C, 22).toMap(),
map(A, 3).put(B, 13).put(C, 23).toMap(),
map(A, 4).put(B, 14).put(C, 24).toMap(),
map(A, 5).put(B, 15).put(C, 25).toMap(),
map(A, 6).put(B, 16).put(C, 26).toMap(),
map(A, 7).put(B, 17).put(C, 27).toMap(),
map(A, 8).put(B, 18).put(C, 28).toMap(),
map(A, 9).put(B, 19).put(C, 29).toMap()
);
// Using a DefaultDataTable
ISortableDataProvider<Map<String,Integer>,String> dataProvider = new SortableDataProvider<Map<String,Integer>,String>()
{
private static final long serialVersionUID = MessageLogStatus.serialVersionUID;
public Iterator<Map<String,Integer>> iterator(long first, long count)
{
int start = Math.max(0, (int) first);
int end = Math.min(data.size(), start + (int) count);
return data.subList(start, end).iterator();
}
public long size()
{
return data.size();
}
public IModel<Map<String,Integer>> model(Map<String,Integer> object)
{
return new CompoundPropertyModel<Map<String,Integer>>(object);
}
};
List<PropertyColumn<Map<String,Integer>,String>> columns = new ArrayList<PropertyColumn<Map<String,Integer>,String>>();
for (String key : keys)
{
columns.add
(
new PropertyColumn<Map<String,Integer>, String>(Model.of(key.toUpperCase()), key)
{
private static final long serialVersionUID = MessageLogStatus.serialVersionUID;
#Override
public void populateItem(Item<ICellPopulator<Map<String, Integer>>> item, String componentId,
IModel<Map<String, Integer>> rowModel)
{
super.populateItem(item, componentId, rowModel);
Map<String, Integer> entity = rowModel.getObject();
String px = getPropertyExpression();
PropertyModel<Object> propModel = new PropertyModel<Object>(rowModel, px);
log.info("Add Label to Cell: PropEx="+px+", Value="+propModel.getObject()+", entity="+entity);
}
}
);
}
//
// Wicket: <table wicket:id="dataTable"></table>
//
DataTable<Map<String,Integer>,String> dataTable =
new DataTable<Map<String,Integer>,String>("dataTable", columns, dataProvider, 5)
{
private static final long serialVersionUID = MessageLogStatus.serialVersionUID;
#Override
protected Item<IColumn<Map<String, Integer>, String>> newCellItem(final String id, final int index,
final IModel<IColumn<Map<String, Integer>, String>> model)
{
final Item<IColumn<Map<String,Integer>, String>> cell = super.newCellItem(id, index, model);
cell.add
(
new AjaxEventBehavior("onclick")
{
private static final long serialVersionUID = MessageLogStatus.serialVersionUID;
#SuppressWarnings("unchecked")
#Override
protected void onEvent(AjaxRequestTarget target)
{
if ( (cell.size() > 0) && (cell.get(0) instanceof Label) )
{
Label cellLabel = (Label) cell.get(0);
PropertyModel<Integer> cellLabelModel = (PropertyModel<Integer>) cellLabel.getInnermostModel();
String property = cellLabelModel.getPropertyExpression();
Integer value = cellLabelModel.getObject();
Map<String, Integer> entity = (Map<String,Integer>) cellLabelModel.getInnermostModelOrObject();
log.info("OnClick: Index="+index+", PropEx="+property+", Value="+value+", Entity="+entity);
}
}
}
);
return cell;
}
};
dataTable.addBottomToolbar(new NavigationToolbar(dataTable));
dataTable.addTopToolbar(new HeadersToolbar<String>(dataTable, null));
add(dataTable);
}
// Make building the data structure a little more fun :)
private MapBuilder<String, Integer> map(String key, Integer value)
{
return new MapBuilder<String, Integer>().put(key, value);
}
private static class MapBuilder<K, V>
{
Map<K, V> map = new HashMap<K, V>();
MapBuilder<K, V> put(K key, V value)
{
map.put(key, value);
return this;
}
Map<K, V> toMap()
{
return map;
}
}
}
Output
OnClick: Index=0, PropEx=alpha, Value=5, Entity={gamma=25, alpha=5, beta=15}
OnClick: Index=1, PropEx=beta, Value=15, Entity={gamma=25, alpha=5, beta=15}
OnClick: Index=2, PropEx=gamma, Value=25, Entity={gamma=25, alpha=5, beta=15}

JavaFX Table Cell Formatting

TableColumn<Event,Date> releaseTime = new TableColumn<>("Release Time");
releaseTime.setCellValueFactory(
new PropertyValueFactory<Event,Date>("releaseTime")
);
How can I change the format of releaseTime? At the moment it calls a simple toString on the Date object.
If you want to preserve the sorting capabilities of your TableColumn, none of the solutions above is valid: if you convert your Date to a String and show it that way in your TableView; the table will sort it as such (so incorrectly).
The solution I found was subclassing the Date class in order to override the toString() method. There is a caveat here though: the TableView uses java.sql.Date instead of java.util.Date; so you need to subclass the former.
import java.text.SimpleDateFormat;
public class CustomDate extends java.sql.Date {
public CustomDate(long date) {
super(date);
}
#Override
public String toString() {
return new SimpleDateFormat("dd/MM/yyyy").format(this);
}
}
The table will call that method in order to print the date.
Of course, you need to change too your Date class in the TableColumn declaration to the new subclass:
#FXML
TableColumn<MyObject, CustomDate> myDateColumn;
Same thing when you attach your object attribute to the column of your table:
myDateColumn.setCellValueFactory(new PropertyValueFactory< MyObject, CustomDate>("myDateAttr"));
And finally, for the shake of clarity this is how you declare the getter in your object class:
public CustomDate getMyDateAttr() {
return new CustomDate(myDateAttr.getTime()); //myDateAttr is a java.util.Date
}
It took me a while to figure out this due to the fact that it uses java.sql.Date behind the scenes; so hopefully this will save other people some time!
Update for Java FX8:
(I'm not sure it is the good place for that answer, but I get the problem in JavaFX8 and some things have changed, like java.time package)
Some differences with the previous answers:
I keep the date type on the column, so I need to use both cellValueFactory and cellFactory.
I Make a generic reusable method to generate the cellFactory for all date columns.
I use java 8 date for java.time package! But the method could be easily reimplemented for java.util.date.
#FXML
private TableColumn<MyBeanUi, ZonedDateTime> dateColumn;
#FXML
public void initialize () {
// The normal binding to column
dateColumn.setCellValueFactory(cellData -> cellData.getValue().getCreationDate());
//.. All the table initialisation and then
DateTimeFormatter format = DateTimeFormatter .ofLocalizedDate(FormatStyle.SHORT);
dateColumn.setCellFactory (getDateCell(format));
}
public static <ROW,T extends Temporal> Callback<TableColumn<ROW, T>, TableCell<ROW, T>> getDateCell (DateTimeFormatter format) {
return column -> {
return new TableCell<ROW, T> () {
#Override
protected void updateItem (T item, boolean empty) {
super.updateItem (item, empty);
if (item == null || empty) {
setText (null);
}
else {
setText (format.format (item));
}
}
};
};
}
The advantages are that:
The column is typed with a "java8 Date" to avoid the sort problem evoqued by #Jordan
The method "getDateCell" is generic and can be used as an util function for all Java8 Time types (Local Zoned etc.)
I'd recommend using Java generics to create re-usable column formatter that takes any java.text.Format. This cuts down on the amount of boilerplate code...
private class ColumnFormatter<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {
private Format format;
public ColumnFormatter(Format format) {
super();
this.format = format;
}
#Override
public TableCell<S, T> call(TableColumn<S, T> arg0) {
return new TableCell<S, T>() {
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
} else {
setGraphic(new Label(format.format(item)));
}
}
};
}
}
Examples of usage
birthday.setCellFactory(new ColumnFormatter<Person, Date>(new SimpleDateFormat("dd MMM YYYY")));
amplitude.setCellFactory(new ColumnFormatter<Levels, Double>(new DecimalFormat("0.0dB")));
I needed to do this recently -
dateAddedColumn.setCellValueFactory(
new Callback<TableColumn.CellDataFeatures<Film, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Film, String> film) {
SimpleStringProperty property = new SimpleStringProperty();
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
property.setValue(dateFormat.format(film.getValue().getCreatedDate()));
return property;
}
});
However - it is a lot easier in Java 8 using Lamba Expressions:
dateAddedColumn.setCellValueFactory(
film -> {
SimpleStringProperty property = new SimpleStringProperty();
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
property.setValue(dateFormat.format(film.getValue().getCreatedDate()));
return property;
});
Hurry up with that Java 8 release oracle!
You can accomplish that through Cell Factories. See
https://stackoverflow.com/a/10149050/682495
https://stackoverflow.com/a/10700642/682495
Although the 2nd link is about ListCell, the same logic is totally applicable to TableCells too.
P.S. Still if you need some sample code, kindly will attach here.
An universal solution could be as simple as that:
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.util.Callback;
public interface AbstractConvertCellFactory<E, T> extends Callback<TableColumn<E, T>, TableCell<E, T>> {
#Override
default TableCell<E, T> call(TableColumn<E, T> param) {
return new TableCell<E, T>() {
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(convert(item));
}
}
};
}
String convert(T value);
}
And its sample usage:
TableColumn<Person, Timestamp> dateCol = new TableColumn<>("employment date");
dateCol.setCellValueFactory(new PropertyValueFactory<>("emploumentDateTime"));
dateCol.setCellFactory((AbstractConvertCellFactory<Person, Timestamp>) value -> new SimpleDateFormat("dd-MM-yyyy").format(value));
This is what i did and i worked perfectly.
tbColDataMovt.setCellFactory((TableColumn<Auditoria, Timestamp> column) -> {
return new TableCell<Auditoria, Timestamp>() {
#Override
protected void updateItem(Timestamp item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item.toLocalDateTime().format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
}
}
};
});
You can easily pipe Properties of different type and put a formatter or converter in between.
//from my model
ObjectProperty<Date> valutaProperty;
//from my view
TableColumn<Posting, String> valutaColumn;
valutaColumn.setCellValueFactory(
cellData -> {
SimpleStringProperty property = new SimpleStringProperty();
property.bindBidirectional(cellData.getValue().valutaProperty, new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN));
return property;
});
The StringConverter classes are another mechanism.
TextFieldTableCell has a constructor as follows: public TextFieldTableCell(StringConverter<T> converter).
... and StringConverters consist of subclasses such as LocalDateStringConverter. A default implementation would then be:
new TextFieldTableCell( new LocalDateStringConverter() );
... this is not bad, but the parameter-less LocalDateStringConverter uses dates of the format 'dd/mm/yyyy' both for parsing (fromString() method) and toString(). But there are other constructors where you can pass a FormatStyle or DateTimeFormatter.
From my experiments, however, StringConverters are slightly problematic in that it is difficult to catch the DateTimeParseException thrown by fromString() with an invalid date.
This can be remedied by creating your own StringConverter class, e.g.:
class ValidatingLocalDateStringConverter extends LocalDateStringConverter {
boolean valid;
#Override
LocalDate fromString(String value) {
valid = true;
if (value.isBlank()) return null;
try {
// NB wants ISO
return LocalDate.parse( value );
} catch ( DateTimeParseException e) {
valid = false;
}
return null;
}
#Override
String toString( LocalDate date ){
// NB returns ISO or the String "null" with null date value (!)
String s = date.toString();
return s.equals( 'null' )? '' : s;
}
}
Using this StringConverter solution will mean that dates are sorted according to chronological order, regardless of the String representation.

Resources