JavaFX TableView with array of TableColumns - java

I got a TableView<MonthRow> where I want to display each month of the year and its relevant days.
I got those three classes for presentation purposes:
public record DayCell(
LocalDate date,
DayType type
) {
public String nameOfDay() {
return DateTimeFormatter.ofPattern("EE", Locale.ENGLISH).format(date);
}
}
public enum DayType {
COMPASSIONATE_LEAVE("C"),
EMPTY("E"),
NOT_ON_PAYROLL("N"),
QUARANTINE("Q"),
REGULAR_LEAVE("R"),
SICK_LEAVE("S"),
TRAINING("TRG"),
TRAVEL("T"),
UNPAID_LEAVE("U");
private final String shortName;
DayType(String shortName) {
this.shortName = shortName;
}
}
public record MonthRow(
String name,
DayCell[] days // amount of days in that specific month
) {
}
And then I create the table content:
public ObservableList<MonthRow> createMonth(int year) {
MonthRow[] months = new MonthRow[12];
for (int i = 0; i < months.length; i++) {
LocalDate date = LocalDate.of(year, i + 1, 1);
months[i] = new MonthRow(date.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH), createCells(date));
}
return FXCollections.observableArrayList(months);
}
public DayCell[] createCells(LocalDate date) {
DayCell[] cells = new DayCell[date.lengthOfMonth()];
for (int i = 0; i < cells.length; i++) {
cells[i] = new DayCell(date.plusDays(i), DayType.EMPTY);
}
return cells;
}
Having the ObservableList now for the TableView, I am kinda stuck. I want to have one TableColumn for the month and 31 TableColumns for the DayCells. However, because it's an array I am not sure how to reflect the cellData of each DayCell into each TableCell.
Also, because some months do not have 31 days and therefore, the
cells should not show any content for the missing days.
Each cell should show the nameOfDay() content and colouring according to the DayType (will be done in the respective cellFactory at some point).
TableView Example
This might be a completely wrong approach, so please don't hesitate to guide me towards a different solution.

You can do something like this:
TableView<MonthRow> table = new TableView<>();
TableColumn<MonthRow, String> monthColumn = new TableColumn<>("Month");
monthColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getName()));
table.getColumns().add(monthColumn);
for (int i = 0; i < 31 ; i++) {
TableColumn<MonthRow, DayCell> dayColumn = new TableColumn<>(Integer.toString(i+1));
final int index = i ;
dayColumn.setCellValueFactory(cellData -> {
MonthRow month = cellData.getValue();
if (index < month.getDays().size()) {
return new SimpleObjectProperty<>(month.getDays()[index]);
} else {
return new SimpleObjectProperty<>(null);
}
});
dayColumn.setCellFactory(new TableCell<>() {
#Override
protected void updateItem(DayCell day, boolean empty) {
super.updateItem(day, empty);
if (empty | day == null) {
setText("");
} else {
setText(day.nameOfDay());
}
}
});
table.getColumns().add(dayColumn);
}
This might be a completely wrong approach
Maybe. Do you need the functionality of a TableView here? E.g. selection of a month (row), etc? Perhaps a simple GridPane inside a ScrollPane would be a more convenient approach; it really depends on your project requirements.

Related

Input Validation Algorithm - Date and Odometer

I'm having a problem making an efficient algorithm that validates my date and odometer input within a given sorted data set. I'm trying to implement a gas mileage tracking program. The sorted data has a date with corresponding odometer value.
Sample Data Set:
Date Odometer Index
2021-2-14 156830 0
2021-2-5 156572 1
2021-2-4 156255 2
Index 0 being the top and recent data entry.
Sample Input:
Date: 2021-2-15
Odometer: 157000
I have to determine which position/order the inputted date belongs inside my data set. Since the user date input is greater than my top/recent date I know this belongs to the very top. Then I compare the odometer from that data to my input. If user odometer input is greater than data odometer then it is valid. If its less than then its invalid.
Another Sample Input:
Date: 2021-2-14
Odometer: 156255
Its okay if the user date input has the same date given in the data set. However, odometer cannot be less than the previously recorded at 156572 on 2021-2-5. So its invalid.
Here is my test input validation algorithm so far:
public static Date[] dates = new Date[3];
public static int[] odometer = new int[3];
public static void main(String[] args)
{
dates[0] = new Date(2021,2,14);
dates[1] = new Date(2021,2,5);
dates[2] = new Date(2021,2,4);
odometer[0] = 156830;
odometer[1] = 156572;
odometer[2] = 156255;
//Inputs
Date inputDate = new Date(2021,2,14);
int inputOdo = 156255;
if(!hasDuplicate(inputDate, inputOdo))//Checks for duplicate
{
int index = -1;
for(int i=0; i<dates.length; i++)
{
if(inputDate.compareTo(dates[i]) >= 0)
{
index = i;
break;
}
}
if(index == 0)
{
if(inputOdo <= odometer[index] && inputDate.compareTo(dates[index]) > 0)
{
System.out.println("Mileage cannot be less than "
+ "your previously recorded fill-up at\n"+odometer[index]+" miles on "+dates[index].toString()+".\n");
}
}else{
if(index > 0)
{
int top = index-1;
int bottom = index;
if(inputOdo >= odometer[top])
{
System.out.println("Mileage cannot be higher than "
+ "your previously recorded fill-up at\n"+odometer[top]+" miles on "+dates[top].toString()+".\n");
}else{
if(inputOdo <= odometer[bottom] && bottom != dates.length-1 && !inputDate.equals(dates[bottom]))
{
System.out.println("Mileage cannot be less than "
+ "your previously recorded fill-up at\n"+odometer[bottom]+" miles on "+dates[bottom].toString()+".\n");
}
}
}else{
int bottom = dates.length-1;
if(inputOdo >= odometer[bottom])
{
System.out.println("Mileage cannot be higher than "
+ "your previously recorded fill-up at\n"+odometer[bottom]+" miles on "+dates[bottom].toString()+".\n");
}
}
}
System.out.println("Gas has been added!");
}else{
System.out.println("Another fill-up with this date and mileage already exist.");
}
hasDuplicate method:
//Checks for duplicate
public static boolean hasDuplicate(Date date, int odo)
{
boolean duplicate = false; //Initialize duplicate variable
//Checks if date and mileage exist already
for(int i=0; i<dates.length; i++)
{
if(date.equals(dates[i]) && odo == odometer[i]) {
return true;
}
}
return duplicate;
}
I hope someone can understand what I am trying to achieve here. Any help and idea will be great! I'm a newbie.
Some changes which you may do-
Instead of having different array of data and odometer, use an object which implements Comparable interface
class MileageData implements Comparable<MileageData> {
LocalDate date;
int odometer;
public MileageData(LocalDate date, int odometer) {
this.date = date;
this.odometer = odometer;
}
#Override
public boolean equals(final Object obj) {
MileageData anotherData = (MileageData)obj;
return anotherData.date.equals(this.date) && anotherData.odometer == this.odometer;
}
#Override
public int hashCode(){
int hash = 7;
hash = 31 * hash + this.date.hashCode();
hash = 31 * hash + this.odometer;
return hash;
}
#Override
public int compareTo(final MileageData o) {
if (o.date.isBefore(this.date)) {
return -1;
} else if (o.date.isAfter(this.date)) {
return 1;
} else {
return 0;
}
}
#Override
public String toString(){
return this.date.toString()+" "+this.odometer;
}
}
Storing MileageData in Treeset which will sort data in the order given in compareTo method of MileageData.
Set<MileageData> data = new TreeSet<>();
data.add(new MileageData(LocalDate.of(2021,2,14),156830));
data.add(new MileageData(LocalDate.of(2021,2,4),156255));
data.add(new MileageData(LocalDate.of(2021,2,4),156255));
data.add(new MileageData(LocalDate.of(2021,2,5),156572));
As Treeset will not store duplicate data, you don't need separate check for duplicates. Above set will return only three objects instead of four.

Trouble with nested for loops and arrays index out of bounds exception [duplicate]

This question already has answers here:
What causes a java.lang.ArrayIndexOutOfBoundsException and how do I prevent it?
(26 answers)
Closed 1 year ago.
I'm writing Java code for an old style phone plan, so I have:
a Band class: public Band(LocalTime startTime, LocalTime endTime, DayOfWeek[] combinedDays, double intervalCost)
a Rate class: public Rate(String name, Band[] bands, int intervalMs, double startCost, String numberRoot)
I want to write a private Band[] selectBandsInDay(DayOfWeek day) method inside the Rate class that, given a day of week, returns an array of Band composed of the bands of that day of week.
What I wrote was:
private Band[] selectBandsInDay(DayOfWeek day) {
Band[] bandsInDay = new Band[bands.length];
int size = 0;
for (int i=0; i<bands.length; i++) {
for (int j=0; j<bands.length; j++) {
if (bands[j].getCombinedDays()[i] == day) {
bandsInDay[size] = bands[i];
size++;
}
}
}
return bandsInDay;
}
But I keep getting an Index Out Of Bounds exception (index 2 out of bounds for length 2).
How could I fix this?
I had to go with a different approach.
private Band[] selectBandsInDay(DayOfWeek day) {
int i = 0;
int length = 0;
for (Band band : bands) {
if(DayOfWeekHelper.isDayIn(day, band.getCombinedDays()))
length++;
}
Band[] bands_1 = new Band[length];
for (Band band : bands) {
if(DayOfWeekHelper.isDayIn(day, band.getCombinedDays()))
bands_1[i++] = band;
}
return bands_1;
}
And I created that DayOfWeekHelper class with isDayIn() method:
public class DayOfWeekHelper {
public static boolean isDayIn(DayOfWeek day, DayOfWeek[] combinedDays) {
for (DayOfWeek d : combinedDays) {
if (d == day) {
return true;
}
}
return false;
}
}

JavaFX TableView how to add items to column?

I've working on a GUI that the user can browse text files from the SYSTEM and then when the user press "Start" button the program reading the text file/s, create lists from its data and supposed to add it to TableView. I'm stuck on inserting the data from the lists to the table. I've created the columns names by file names and added it to table:
tblConfigurationSystemColumns.add("Parameter Name");
tblSystemColumn.stream().map((str) -> str.split("PCM")).forEachOrdered((a) -> {
tblConfigurationSystemColumns.add(a[0].trim());
});
for (int i = 0; i < tblConfigurationSystemColumns.size(); i++) {
TableColumn col = new TableColumn(tblConfigurationSystemColumns.get(i));
tableConfigurationSystem.getColumns().addAll(col);
}
The column names coming from the list tblConfigurationSystemColumns. This list may be changed from each use of the GUI by number of file you browse from the system. (for now let think that we have 2 strings inside: "column1","column2")
I need to add items to column1 from the list SysParameter , and to column2 from list SysValues.
How can I add values from each list to each column by rows?
If you need any more code please tell me (just let you know, the only code that I have it the list creating from the files).
EDIT:
This is what I got after the column building.
after this I need to get the "Parameter" and the "Value" for each column(as you can see).
I've made a list that get the "Parameter" from the text file, and another list that get the "Value" from the text file.
how can I put each list to it's column?
This is the code that build this lists:
boolean inCESystem = false;
for (final String line : list) {
if (line.contains("CE-") && !(line.contains("CE-system-equipment-pm") || line.contains("inbound") || line.contains("outbound"))) {
inCESystem = true;
}
else if (line.trim().isEmpty()) {
inCESystem = false;
}
else if (inCESystem) {
CE_System.add(line);
}
}
boolean inCESystemInbound = false;
for (final String line : list) {
if (line.contains("CE-") && (line.contains("inbound")) ) {
inCESystemInbound = true;
}
else if (line.trim().isEmpty()) {
inCESystemInbound = false;
}
else if (inCESystemInbound) {
CE_System.add("inbound_loadlock - "+line.trim());
}
}
boolean inCESystemOutbound = false;
for (final String line : list) {
if (line.contains("CE-") && (line.contains("outbound")) ) {
inCESystemOutbound = true;
}
else if (line.trim().isEmpty()) {
inCESystemOutbound = false;
}
else if (inCESystemOutbound) {
CE_System.add("outbound_loadlock - "+line.trim());
}
}
/*
* Check the CE list to split each object per parameter and value to different lists
*/
CE_System.stream().map((str) -> str.split(",")).map((a) -> {
CE_SystemParameter.add(a[0].trim()); //Parameters
return a;
}).forEachOrdered((a) -> {
if(a.length > 1) {
CE_System_Value.add(a[1].trim()); //Values
} else {
CE_System_Value.add(""); //add blank if parameter doesn't have value
}
});
EDIT 2: Text file example
CE-system:
No features to set for this item...
CE-system-componentmanager:
Bootstrap Parallelism ,Parallel Bootstrapping
CE-system-components:
No features to set for this item...
CE-system-components-accessmanager:
Access control enable ,disabled
Access policy prototyping ,enabled
Access user group ,enabled
Implicit roles access policy ,disabled
World access policy ,disabled
CE-system-components-eqlog:
EquipmentLog Enable ,false
Line that contains "CE-" its just title to know that is should be in the "Configuration" Tab.
each line inside is the "parameter" and the value(after the comma).
EDIT 3: The table should look like this example (This example is from my code in Java SWT)
Thank you very much guys.
The data for a TableView is held in the ObservableList of the items property. A TableView is designed to hold a list of POJOs that contain various properties. Each of the properties will correspond to a TableColumn who obtains the value of these properties using a Callback.
Since you are browsing text files let's say you define a POJO like so:
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.property.SimpleStringProperty;
public class TextFile {
private final StringProperty name = new SimpleStringProperty(this, "name");
public final void setName(String name) { this.name.set(name); }
public final String getName() { return name.get(); }
public final StringProperty nameProperty() { return name; }
private final LongProperty size = new SimpleLongProperty(this, "size");
public final void setSize(long size) { this.size.set(size); }
public final long getSize() { return size.get(); }
public final LongProperty sizeProperty() { return size; }
public TextFile() {}
public TextFile(String name, long size) {
setName(name);
setSize(size);
}
}
From this you'll want a TableView of TextFiles that has a TableColumn for name and a TableColumn for size. To tell a TableColumn how to obtain the correct value you set the cellValueFactory with the appropriate Callback. This Callback accepts a TableColumn.CellDataFeatures and returns an ObservableValue. If the ObservableValue changes the TableColumn will update the item of the corresponding TableCell.
ObservableList<TextFile> files = ...;
TableView<TextFile> table = new TableView<>();
table.setItems(files);
TableColumn<TextFile, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(features -> features.getValue().nameProperty());
table.getColumns().add(nameCol);
TableColumn<TextFile, Number> sizeCol = new TableColumn<>("Size");
sizeCol.setCellValueFactory(features -> features.getValue().sizeProperty());
table.getColumns().add(sizeCol);
Note that each TextFile in files is a row in the TableView.
I guess you are looking for something like that:
TableColumn< YourObject, String> col = new TableColumn<>();
col.setCellValueFactory(new PropertyValueFactory("nameOfThePropertyYouWantToDisplay");
TableColumn< YourObject, String> col2 ....
TableView < YourObject> table = new TableView();
table.setItems(observableListOfYourObject);
Look here for a detailed description: https://docs.oracle.com/javafx/2/ui_controls/table-view.htm
Choose the item type accordingly. Your description indicates the following properties:
The table data is not edited once it's loaded.
You cannot hardcode the number of files.
Therefore a suitable choice of data structure would be List<String>. Each list contains one element for every column.
public void initializeTableColumns(TableView<List<String>> table, File file, File... files) {
List<String> fileItems = readFile(file);
TableColumn<List<String>, String> column = new TableColumn<>(file.getName());
column.setCellValueFactory(cd -> new SimpleStringProperty(cd.getValue().get(0));
table.getColumns().add(column);
for (String s : fileItems) {
List<String> row = new ArrayList<>(files.length + 1);
row.add(s);
table.getItems().add(row);
}
for (int fileIndex = 0; fileIndex < files.length; fileIndex++) {
File f = files[fileIndex];
fileItems = readFile(f);
int itemCount = Math.min(fileItems.size(), table.getItems().size());
// add items from file
for (int i = 0; i < itemCount; i++) {
table.getItems().get(i).add(fileItems.get(i));
}
if (itemCount <= table.getItems.size()) {
// fill items that may be missing
for (int i = itemCount; i < table.getItems().size(); i++) {
table.getItems().get(i).add(null);
}
} else {
// add missing rows
for (int i = table.getItems.size(); i < itemCount; i++) {
List<String> row = new ArrayList<>(files.length + 1);
for (int j = 0; j <= fileIndex; j++) {
row.add(null);
}
row.add(fileItems.get(i));
table.getItems().add(row);
}
}
final index = fileIndex + 1;
column = new TableColumn<>(f.getName());
column.setTableColumn(cd -> new SimpleStringProperty(cd.getValue().get(index)));
table.getColumns().add(column);
}
}

Codename one calendar UpdateButtonDayDate() issue

I am working on my project where I want to show when application starts then calendar display, which date contain events, for instance if the date contain events, then the day button contains * symbol and day, And if the date doesn't contain any event then it only displays a day.
I wrote following code, but it only displays * symbol when I am clicking on that button, So how can I manage this code that display * symbol on the date which only contain events when the application starts or that page gonna be loaded.
Following is my code:-
public class Customised extends Calendar{
ArrayList<String[]> data = new ArrayList<>();
int i,j,columns;
#Override
protected void updateButtonDayDate(Button dayButton,int currentMonth, int day) {
dayButton.setText(""+day);
dayButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
//Check which date having how many number of events===============================================================
try{
ShowEvent.removeAll();
cur = db.executeQuery("SELECT Event, Description from CalendarData WHERE Date = ? ", dateLabel.getText());
columns = cur.getColumnCount();
if(columns > 0) {
boolean next = cur.next();
if(next) {
String[] columnNames = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
columnNames[iter] = cur.getColumnName(iter);
}
while(next) {
Row currentRow = cur.getRow();
String[] currentRowArray = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
currentRowArray[iter] = currentRow.getString(iter);
}
data.add(currentRowArray);
next = cur.next();
}
Object[][] arr = new Object[data.size()][];
data.toArray(arr);
}
}
}catch(IOException e){
e.printStackTrace();
}
for(i = 0 ; i< data.size(); i++){
Log.p(data.get(i)[0]);
}
Label a = new Label(dateLabel.getText());
Label b = new Label(" "+i);
Container container1 = TableLayout.encloseIn(2, a,b);
container1.setUIID("container1");
ShowEvent.add(container1);
for( i = 0 ; i< data.size(); i++){
for(j = 0; j<columns; j++){
Log.p(data.get(i)[j]);
SpanLabel spanData = new SpanLabel(data.get(i)[j]);
spanData.setUIID("SpanLabel");
ShowEvent.add(spanData);
}
Label space = new Label("=======================");
ShowEvent.add(space);
Log.p("###################");
}
data.clear();
if(i>0){
if(Dialog.show("Choose action", "What you want to do?", "Add Events","View Events")){
calendar.show();
}
else{
ShowEvent.show();
}
}else{
Dialog.show("Add event","There is no event to display, Please add events first","OK","");
}
//============================================================================================================
}
});
}
#Override
protected void initComponent(){
ArrayList<String[]> data1 = new ArrayList<>();
int k;
Log.p("initComponent");
try{
cur = db.executeQuery("select Date from CalendarData");
columns = cur.getColumnCount();
if(columns > 0) {
boolean next = cur.next();
if(next) {
String[] columnNames = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
columnNames[iter] = cur.getColumnName(iter);
}
while(next) {
Row currentRow = cur.getRow();
String[] currentRowArray = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
currentRowArray[iter] = currentRow.getString(iter);
}
data1.add(currentRowArray);
next = cur.next();
}
Object[][] arr = new Object[data1.size()][];
data1.toArray(arr);
}
}
}catch(IOException e){
e.printStackTrace();
}
for(k = 0 ; k< data1.size(); k++){
Log.p(data1.get(k)[0]);
}
if(k>0){
//cal.setUIID("CalendarSelectedDay");
}
}
/*
#Override
protected boolean isInitialized(){
boolean result = false;
Log.p("isInitialised");
return result;
}*/
public Customised(){
}
#Override
protected Button createDay() {
Button day = new Button();
day.setAlignment(CENTER);
day.setUIID("CalendarDay1");
day.setEndsWith3Points(false);
day.setTickerEnabled(false);
return day;
}
}
And the expected result will be:-
That's because you placed the code inside the actionPerformed method which is only triggered upon Button pressed/released.
Move your code to the updateButtonDayDate scope

Lists in Java - Adding values to their keys (HashMap or HashSet or other)

I want to sort my LineChart X axis in JavaFX. I have Dates(X axis) from DatePicker and their Values(Y axis), but there are for exaple four exactly the same dates and different values. What I want to do is that I need to check if date exist, and if yes, I want to add the value to that date. Sorry about my english.
Look at my Linechart.
The first date has three values. I want to add them.
here is my code:
void initLineChart()
{
//defining a series
XYChart.Series<String,Number> series = new XYChart.Series<String, Number>();
lineChart.setAxisSortingPolicy(LineChart.SortingPolicy.X_AXIS);
String date = new String();
int numb;
String value = new String();;
ShowDreamHistoryController.save();
ShowDreamHistoryController.loadDreamAtStart();
for (int i = 0; i < ShowDreamHistoryController.listDreams.size(); i++) {
date = ShowDreamHistoryController.listDreams.get(i).getDate().toString();
value = ShowDreamHistoryController.listDreams.get(i).getHours();
if(value != null)
{
numb = Integer.valueOf(value);
series.getData().add(new XYChart.Data<String,Number>(date, numb));
}
}//for
// System.out.println(datesOnes);
lineChart.getData().add(series);
}
Check if the date exists in series and if it does, remove that index and add to it.
int indexExist = existAt(series,date);
if(indexExist < 0){ // if the date does not exist
series.getData().add(new XYChart.Data<String, Number>(date, numb));
} else { //if the date exists
int curVal = series.getData().get(indexExist).getYValue().intValue();
//get the current value stored in that date
series.getData().remove(indexExist);
// remove the index
series.getData().add(indexExist, new XYChart.Data<String,Number>(date, curVal + value));
// add to that index ( current value + value )
}
Then we would have a function that looks for the index.
/*
Loop through "SERIES" and return the position of the string
-1 if it doesn't exist.
*/
public int existAt(XYChart.Series <String, Number> series, String date){
for(int i=0; i<series.getData().size(); i++){
if(series.getData().get(i).getXValue().equals(date)){
return i;
}
}
return -1;
}

Categories