Hi StackOverflow people,
First question here, I'm stuck on this code and cannot move forward, tried different approaches but cannot figure out why this is happening.
The code is intended to be a few lists each one represents a day of the week, and each of the list has all the possible time. Now, everytime I ran the code each list, even when not update, is using the last date available. For the sake of the example, remove almost all the lists and leave only 2.
The update on the date is being done on this line, t1.setFecha(lunesDate.plusDays(i));, but if for instance, I remove this line on one of the lists, the list is getting the date updated, even if this is happening on another list, with another variable!! It is like the JVM is considering all the lists to be the same... Makes no sense for me...
Can anyone point where is the issue on the code?
Class Turno.class
import java.time.LocalDate;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
public class Turno {
private static final int LIBRE = 1;
private static final int RESERVADO = 2;
private static final int CUMPLIDO = 3;
private static final int CERRADO = 4;
private SimpleIntegerProperty id = new SimpleIntegerProperty();
private SimpleObjectProperty<LocalDate> fecha = new SimpleObjectProperty<LocalDate>();
private SimpleIntegerProperty idhorario = new SimpleIntegerProperty();
private SimpleStringProperty horario = new SimpleStringProperty();
private SimpleIntegerProperty estado = new SimpleIntegerProperty();
private SimpleIntegerProperty idProfesional = new SimpleIntegerProperty();
private SimpleStringProperty profesional = new SimpleStringProperty();;
private SimpleIntegerProperty idPaciente = new SimpleIntegerProperty();
private SimpleStringProperty paciente = new SimpleStringProperty();;
private SimpleStringProperty observaciones = new SimpleStringProperty();;
public Turno(int id, LocalDate d, int idh, String h, int e, int idpro, String pro, int idpac, String pac,
String o) {
this.setId(id);
this.setFecha(d);
this.setIdHorario(idh);
this.setHorario(h);
this.setEstado(e);
this.setIdProfesional(idpro);
this.setProfesional(pro);
this.setIdPaciente(idpac);
this.setPaciente(pac);
this.setObservaciones(o);
}
public Turno() {
}
// ID
public final SimpleIntegerProperty idProperty() {
return this.id;
}
public final int getId() {
return this.idProperty().get();
}
public final void setId(final int i) {
this.idProperty().set(i);
}
/* Bunch of getter and setters for properties, just like the one above */
Class TestTurno.class
public class TestTurnos extends Application {
private static Turno turnoSeleccionado = null;
ScrollPane scrollPane = new ScrollPane();
HBox listas = new HBox();
VBox vBoxL = new VBox();
VBox vBoxM = new VBox();
ListView<Turno> listViewTurnosL = new ListView<>();
ListView<Turno> listViewTurnosM = new ListView<>();
List<Turno> listaHorarios = new ArrayList<>();
List<Turno> listaTurnos = new ArrayList<>();
public static void Main(String[] args) {
launch(args);
}
#Override
public void start(Stage escenario) throws Exception {
// Here we get the current Monday date, in order to prepare for the current week
LocalDate lunesDate = null;
LocalDate diaSemana = null;
LocalDate diaHoy = LocalDate.now();
int d = diaHoy.getDayOfWeek().getValue();
lunesDate = diaHoy.minusDays(d - 1);
// Give the schedules to each day list
listViewTurnosL.setItems(FXCollections.observableList(listaHorarios));
listViewTurnosM.setItems(FXCollections.observableList(listaHorarios));
// Then we look for more data on the DB,
for (int i = 0; i < 2; i++) {
// Database magic happens here, we filled the listaTurnos, not relevant
// We make the lists
//
//!!! Here is where the glitch appears, debugging shows that it only gets into the switch on the right conditions,
// but it keeps on updating the date on any of the lists, even when it is updating another list
switch (i) {
case 0: {
for (Turno t1 : listViewTurnosL.getItems()) {
t1.setFecha(lunesDate.plusDays(i));
}
// Value of t1.getFecha() is 1
break;
}
case 1: {
for (Turno t2 : listViewTurnosM.getItems()) {
t2.setFecha(lunesDate.plusDays(i));
}
// Value of t1.getFecha() is 2 !!!!!
// Value of t2.getFecha() is 2
break;
}
}
}
vBoxL.getChildren().addAll(listViewTurnosL);
vBoxM.getChildren().addAll(listViewTurnosM);
listas.getChildren().addAll(vBoxL, vBoxM);
scrollPane.setContent(listas);
Scene escena = new Scene(scrollPane, 800, 800);
escenario.setScene(escena);
escenario.show();
}
}
Consider how you are creating your lists:
listViewTurnosL.setItems(FXCollections.observableList(listaHorarios));
listViewTurnosM.setItems(FXCollections.observableList(listaHorarios));
The documentation for the factory method you are using reads:
Constructs an ObservableList that is backed by the specified list.
That is - the base list provided is kept as the backing (storing) list. Since both ObservableList instances share the same original ArrayList, it is no wonder they share the content.
You may want to use the factory method FXCollections.ObservableArrayList which creates a new ObservableList (the backing list is created internally, or is the list itself).
If you really need the non-observable list instances, you should probably use two different ones if the lists are not to be equal.
If I look at your code I see the following things:
listViewTurnosL.setItems(FXCollections.observableList(listaHorarios));
listViewTurnosM.setItems(FXCollections.observableList(listaHorarios));
This means to that both listViewTurnosX contain exactly the same element references.
case 0: {
for (Turno t1 : listViewTurnosL.getItems()) {
t1.setFecha(lunesDate.plusDays(i));
}
// Value of t1.getFecha() is 1
break;
}
case 1: {
for (Turno t2 : listViewTurnosM.getItems()) {
t2.setFecha(lunesDate.plusDays(i));
}
It doesn't matter which list is iterated, both contain the same elements, so in both cases the same properties get updated.
Related
My issue is the following : I read a .csv file and convert it into an ArrayList. In order to put this ArrayList in a javafx TableView, I need to create an object, go through ObservableList<Object> data = FXCollections.observableArrayList() and populate it with as much as new Object() as I need. The point is that in my .csv, the number of columns may differ in each run of my program, so the object definition does. I don't know how to define an object which definition is a function of the length of my ArrayList and the attributes aren't always the same (and I can't find any clue on google).
To illustrate, imagine the following code :
public class SettingData extends Application {
public void start(Stage stage) {
TableView<FileData> table = new TableView<FileData>();
final ObservableList<FileData> data = FXCollections.observableArrayList(
new FileData("file1", "D:\\myFiles\\file1.txt", "25 MB", "12/01/2017"),
new FileData("file2", "D:\\myFiles\\file2.txt", "30 MB", "01/11/2019"),
new FileData("file3", "D:\\myFiles\\file3.txt", "50 MB", "12/04/2017"),
new FileData("file4", "D:\\myFiles\\file4.txt", "75 MB", "25/09/2018")
);
TableColumn fileNameCol = new TableColumn("File Name");
fileNameCol.setCellValueFactory(new PropertyValueFactory<>("fileName"));
TableColumn pathCol = new TableColumn("Path");
pathCol.setCellValueFactory(new PropertyValueFactory("path"));
TableColumn sizeCol = new TableColumn("Size");
sizeCol.setCellValueFactory(new PropertyValueFactory("size"));
TableColumn dateCol = new TableColumn("Date Modified");
dateCol.setCellValueFactory(new PropertyValueFactory("dateModified"));
ObservableList<String> list = FXCollections.observableArrayList();
table.setItems(data);
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
table.getColumns().addAll(fileNameCol, pathCol, sizeCol, dateCol);
Assuming that we define FileData like this :
public class FileData {
SimpleStringProperty fileName;
SimpleStringProperty path;
SimpleStringProperty size;
SimpleStringProperty dateModified;
FileData(String fileName, String path, String size, String dateModified) {
this.fileName = new SimpleStringProperty(fileName);
this.path = new SimpleStringProperty(path);
this.size = new SimpleStringProperty(size);
this.dateModified = new SimpleStringProperty(dateModified);
}
public String getFileName(){
return fileName.get();
}
public void setFileName(String fname){
fileName.set(fname);
}
public String getPath(){
return path.get();
}
public void setPath(String fpath){
path.set(fpath);
}
public String getSize(){
return size.get();
}
public void setSize(String fsize){
size.set(fsize);
}
public String getDateModified(){
return dateModified.get();
}
public void setModified(String fmodified){
dateModified.set(fmodified);
}
}
How may implement my code to handle the fact that once I may not have size columns, or another time I'll have a author column. (In fact, the file I'm working with carries around 80 to 110 columns, that's why I need to have a "modular" definition of my object).
It's my first post on any forum ever, I'm not familiar with your usages, sorry for that.
Thanks for your answers.
I'm trying to populate a TableView in JavaFX, but only one of the columns is being populated with Data. I've been following the oracle documentation and think that my name conventions are correct.
Data Model:
import javafx.beans.property.SimpleIntegerProperty;
public class PeakClassification {
private final SimpleIntegerProperty peakStart;
private final SimpleIntegerProperty peakEnd;
private final SimpleIntegerProperty peakMaxima;
private final SimpleIntegerProperty peakHeight;
private final SimpleIntegerProperty peakWidth;
public PeakClassification(int peakStart, int peakEnd, int peakMaxima, int peakHeight) {
this.peakStart = new SimpleIntegerProperty(peakStart);
this.peakEnd = new SimpleIntegerProperty(peakEnd);
this.peakMaxima = new SimpleIntegerProperty(peakMaxima);
this.peakHeight = new SimpleIntegerProperty(peakHeight);
this.peakWidth = new SimpleIntegerProperty(peakEnd - peakStart);
}
public int getPeakWidth() {
return peakWidth.get();
}
public int getPeakHeight() {
return peakHeight.get();
}
public int getPeakStart() {
return peakStart.get();
}
public int getPeakEnd() {
return peakEnd.get();
}
public int getPeakMaxima() {
return peakMaxima.get();
}
}
Code for creating the table:
//instantiate the table
TableView tableView = new TableView();
//start values
TableColumn startValue = new TableColumn("Start pos");
startValue.setMinWidth(100);
startValue.setCellValueFactory(new PropertyValueFactory<PeakClassification, Integer>("peakStart"));
TableColumn maximumValue = new TableColumn("Max pos");
startValue.setCellValueFactory(new PropertyValueFactory<PeakClassification, Integer>("peakMaxima"));
tableView.setItems(peakClassifications);
tableView.getColumns().addAll(startValue, maximumValue);
I've printed out the peakClassifications list to console to verify that the maximumValue isn't null.
The getter for the peakMaxima field is getPeakMaxima() so it should be able to find it. I've looked at other stackoverflow entries and that seems to be the issue in most of the cases.
Here's a snippet of the result:
Its probably an obvious mistake. Any ideas?
Thanks.
Error is here, you mistankenly used startValue variable again instead of maximumValue
TableColumn maximumValue = new TableColumn("Max pos");
startValue.setCellValueFactory(new PropertyValueFactory<PeakClassification, Integer>("peakMaxima"));
Why is my TableColumn.setSortable() showing the sort graphic on the table header when I double-click on it, but it is not actually doing any sort at all? I would imagine it naturally knows how to sort Numbers? Do I have to set an explicit comparator even for types that have a natural sort behavior?
public class PenaltyDashboardManager {
private final TableView<Penalty> penaltyTable = new TableView<Penalty>();
/* ... */
private void initializeTable() {
penaltyTable.setItems(Penalty.getPenaltyManager().getPenalties());
penaltyTable.setEditable(true);
TableColumn<Penalty,Number> penaltyId = new TableColumn<>("ID");
penaltyId.setCellValueFactory(c -> c.getValue().getPenaltyIdProperty());
penaltyId.setEditable(true);
penaltyId.setSortable(true);
/* ... */
penaltyTable.getColumns.add(penaltyId);
}
}
UPDATE
Very odd. I tried to create a simple example to demonstate the sorting not working. But this simple column of integers is sorting just fine :/
public final class TableSortTest extends Application {
private static final ObservableList<NumericCombo> values = FXCollections.observableList(
IntStream.range(1, 100).mapToObj(i -> new NumericCombo()).collect(Collectors.toList()));
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Collections.shuffle(values);
TableView<NumericCombo> tableView = new TableView<>();
tableView.setItems(values);
TableColumn<NumericCombo,Number> combo1 = new TableColumn<>("COMBO 1");
combo1.setCellValueFactory(c -> new ReadOnlyObjectWrapper<>(c.getValue().combo1));
TableColumn<NumericCombo,Number> combo2 = new TableColumn<>("COMBO 2");
combo2.setCellValueFactory(c -> c.getValue().combo2);
TableColumn<NumericCombo,Number> combo3 = new TableColumn<>("COMBO 3");
combo3.setCellValueFactory(c -> c.getValue().combo3);
tableView.getColumns().addAll(combo1,combo2,combo3);
Group root = new Group(tableView);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private static final class NumericCombo {
private static final Random rand = new Random();
private final int combo1;
private final IntegerProperty combo2;
private final IntegerProperty combo3;
private NumericCombo() {
combo1 = rand.nextInt((10000 - 0) + 1);
combo2 = new SimpleIntegerProperty(rand.nextInt((10000 - 0) + 1));
combo3 = new SimpleIntegerProperty(rand.nextInt((10000 - 0) + 1));
}
}
}
I found the issue! I was using my own implementation of ObservableList, called ObservableImmutableList. It wraps the ObservableList interface around a Guava ImmutableList. Since the ImmutableList is not modifiable, it cannot be sorted... even in a TableView.
This transitions to another issue I'm struggling to figure out. How do I sort my ObservableImmutableList? So I posted another question.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
OK i have multiple Java files but only looking at 2 at the moment and i am doing a GUI program.
I have an arraylist of Landlords in one file with a getLandlords method that returns the array and it won't work eclipse just gives an error no matter what i try
File 1
package assignment2;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
public class RealEstateManage extends JFrame implements ActionListener, ListSelectionListener{
public static void main(String[] args)
{
RealEstateManage theGUI = new RealEstateManage();
}
//Initial setup for base of GUI
private JFrame jfFrame;
private JButton jbAddLandlord, jbAddProperty, jbAddLease, jbRecord, jbMaintenance, jbDisplayproperty;
private JTextArea jtaResults;
private JTextField jtfinput1, jtfinput2, jtfinput3, jtfinput4;
private JList <Landlord> jlLandlord;
private Vector<Landlord> vlist = new Vector<Landlord>();
private JPanel jpButtons = setupButtons(), jpResults = setupResults();
public RealEstateAgency R = new RealEstateAgency();
public RealEstateManage()
{
jfFrame = new JFrame ();
jfFrame.add(jpButtons, "North");
jfFrame.add(jpResults,"West");
jfFrame.setSize(900, 400);
jfFrame.setLocation(400, 300);
jfFrame.setTitle("Real Estate Management");
jfFrame.setVisible(true);
}
//Setup the buttons at the top of the GUI
public JPanel setupButtons()
{
JPanel jp = new JPanel ();
jbAddLandlord = new JButton("Add New Landlord");
jbAddProperty = new JButton("Add New Property");
jbAddLease = new JButton("Add New Lease");
jbRecord = new JButton("Record Payment");
jbMaintenance = new JButton("Maintenance");
jbDisplayproperty = new JButton("Display Properties");
jp.add(jbAddLandlord);
jp.add(jbAddProperty);
jp.add(jbAddLease);
jp.add(jbRecord);
jp.add(jbMaintenance);
jp.add(jbDisplayproperty);
jbAddLandlord.addActionListener(this);
jbAddProperty.addActionListener(this);
jbAddLease.addActionListener(this);
jbRecord.addActionListener(this);
jbMaintenance.addActionListener(this);
jbDisplayproperty.addActionListener(this);
return jp;
}
public JPanel setupResults()
{
JPanel jp = new JPanel ();
vlist.add(new Landlord("Fred Jones", "23 Hamilton Road", "0458 789 456", 456123369));
jtfinput1 = new JTextField (10);
jtfinput2 = new JTextField (10);
jtfinput3 = new JTextField (10);
jtfinput4 = new JTextField (10);
ArrayList<Landlord> Alist = R.getLandlords();
jlLandlord = new JList<Landlord>(vlist);
jlLandlord.addListSelectionListener(this);
jlLandlord.setPreferredSize(new Dimension(250, 300));
JLabel jlResults = new JLabel ("Output!");
jtaResults = new JTextArea (18, 30);
jtaResults.setEnabled(false);
jp.add(jlLandlord);
jp.add(jlResults);
jp.add(jtaResults);
return jp;
}
//List Events
#Override
public void valueChanged(ListSelectionEvent arg0) {
// TODO Auto-generated method stub
}
//Button Action Events
public void actionPerformed(ActionEvent ae) {
// Button Events
if(ae.getSource() == jbAddLandlord)
{
addLandlord();
}
if(ae.getSource() == jbAddLandlord)
{
}
}
//Add landlord function
public void addLandlord()
{
boolean inputIsOk = true;
String stName, stAddress, stNum, stBank;
long bank = 0;
JPanel myPanel = new JPanel();
myPanel.add(new JLabel("Name:"));
myPanel.add(jtfinput1);
myPanel.add(new JLabel("Address:"));
myPanel.add(jtfinput2);
myPanel.add(new JLabel("Phone:"));
myPanel.add(jtfinput3);
myPanel.add(new JLabel("Bank Account: (8-10 Digits)"));
myPanel.add(jtfinput4);
int result = JOptionPane.showConfirmDialog(null, myPanel,
"Please Enter X and Y Values", JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
stName = jtfinput1.getText();
stAddress = jtfinput2.getText();
stNum = jtfinput3.getText();
stBank = jtfinput4.getText();
try{bank = Long.valueOf(stBank).longValue();
}
catch(NumberFormatException nfe){
inputIsOk = false;
}
if (inputIsOk = true){
R.addLandlord(new Landlord(stName, stAddress, stNum, bank));
}
}
// jlLandlord.updateUI();
}
}
FILE 2
package assignment2;
import java.util.*;
// The RealEstateAgency class is intended to be the central place to go to for finding
// a landlord that the system "knows" about.
public class RealEstateAgency {
private ArrayList<Landlord> allLandlords; // A way of collecting all the Landlords
public RealEstateAgency()
{
// prepare the agency for future landlords...
allLandlords = new ArrayList<Landlord>();
addLandlord(new Landlord("stName", "stAddress", "stNum", 12456897));
System.out.println(getLandlords());
}
// Method to note the provided Landlord, by adding it to the RealEstateAgency's internal ArrayList.
// Returns true if apparently successful, false if something goes wrong such as the landlord already being present.
public boolean addLandlord(Landlord whoToStore)
{
boolean success = false;
if (whoToStore == null) // ensure there is actually a parameter
success = false;
else {
if (allLandlords.contains(whoToStore)) // landlord already in the list
success = false;
else
{ // Landlord not yet in list
allLandlords.add(whoToStore); // So add this landlord
success = true;
}
}
return success;
}
// Method to obtain a landlord from the RealEstateAgency's collection of known landlords,
// by providing the full name. If no such landlord exists, null will be returned.
public Landlord getLandlord(String fullName)
{
Landlord current = null;
boolean found = false;
int index = 0;
while ((!found) && (index < allLandlords.size()))
{
current = allLandlords.get(index);
if (current.getFullName().equals(fullName))
found = true;
else
index++;
}
// If we get to here, and "found" is true, then "current" will be the matching Landlord
if (!found) // did not find a match
current = null; // so ensure we return no Landlord
return current;
}
// Method to obtain an ArrayList containing the same landlords as those which the
// RealEstateAgency knows about (but without exposing the inner ArrayList object
// so that any alterations made to the returned ArrayList won't impact the RealEstateAgency)
public ArrayList<Landlord> getLandlords()
{
ArrayList<Landlord> duplicate;
duplicate = (ArrayList<Landlord>) allLandlords.clone();
return duplicate;
}
public ArrayList<Landlord> getLandlords1()
{
return allLandlords;
}
}
Any help or suggestions would be appreciated.
The Issues i am having is in the setupResults() function and the getLandlords() function
Thanks in advance
LANDLORD CODE
package assignment2;
import java.util.*;
// The Landlord class allows you to create objects that encapsulate the key data
// about each Landlord, as well as the information about all the properties that
// they make available for rent (some of which may have current leases active).
public class Landlord {
private String fullName; // The name of the landlord
private String address; // Where the landlord lives!
private String phone; // allow for spaces in number
private long bankAccount; // To permit 10 digits, requires the 'long' data type
private ArrayList<Property> myProperties; // All properties owned by this landlord
public Landlord(String fullName, String address, String phone, long bankAccount)
{
// Simply store the parameters into the fields...
this.fullName = fullName;
this.address = address;
this.phone = phone;
this.bankAccount = bankAccount;
// prepare for future leases...
myProperties = new ArrayList<Property>();
}
// ACCESSORS for each field of basic information
public String getFullName()
{
return fullName;
}
public String getAddress()
{
return address;
}
public String getPhone()
{
return phone;
}
public long getBankAccount()
{
return bankAccount;
}
// Method to note/store another Property, where this Landlord is considered the owner of the property.
// It returns true if successfully added to the list of properties belonging to this landlord.
public boolean addProperty(Property theProperty)
{
boolean result;
if (theProperty == null) // if it is null, we ignore this method call.
result = false;
else if (!myProperties.contains(theProperty)){ // Make sure it is not already in the array list
myProperties.add(theProperty);
result = true;
}
else // This means w have already added the property, so cannot add it again.
result = false;
return result;
}
// Method to return an iterator that may be used to cycle over all leases involving this landlord.
public Iterator<Property> getPropertiesIterator()
{
return myProperties.iterator();
}
public String toString()
{
return "Landlord: " + fullName;
}
}
PROPERTY CODE
package assignment2;
// The Property class encapsulates the basic information about one property the system knows
// about: essentially the address of the property, and a reference to the Lease details of the property.
public class Property {
private String propertyAddress;
private Lease currentLease; // When null, there is no active lease (it is available).
public Property(String address)
{
this.propertyAddress = address;
this.currentLease = null; // Initially, nobody is leasing it.
}
public String getPropertyAddress()
{
return propertyAddress;
}
// no mutator for address, because it won't ever change.
// Method to return the current lease's details.
public Lease getCurrentLease()
{
return currentLease;
}
// Method to set the current Lease of this Property.
// If null is provided, the property will not have any Lease assigned to it;
// otherwise, the provided Lease will be recorded as describing the relationship for this property.
public void setCurrentLease(Lease newLease)
{
currentLease = newLease;
}
// Method to report whether or not this property is currently associated with a lease (regardless
// of whether fully paid or not)
public boolean isLeased()
{
if (currentLease == null)
return false;
else
return true;
}
public String toString()
{
return "Property: " + propertyAddress;
}
}
LEASE CODE
package assignment2;
import java.util.*;
// The Lease class encapsulates information about an individual lease of a property by a tenant.
// The data stored here includes all the tenant’s details (name, phone number) as well as
// characteristics of the lease itself such as the duration, the weekly rent amount, how long
// remains in terms of payments, what maintenance has occurred.
public class Lease {
private String tenantFullName; // The name of the person who is living in the leased property.
private String tenantPhone; // Contact number for the person. Allow a leading zero at start, spaces between sections.
private int rentRate; // The amount of rent to be paid each week.
private int leasePeriod; // Either 6 or 12, being a 6 month or 12 month agreement
private int weeksRemaining; // How many weeks remain to be paid for. Initially 26 or 52.
private ArrayList<Maintenance> maintenanceRecord; // record of all maintenance during life of lease
// Create a new lease, recording the following details:
// - tenantName: records the name of the person living in the property
// - tenantPhone: records a contact phone number for the tenant
// - rentRate: the amount of rent which is to be paid each week.
// - leasePeriod: Either 6 or 12, indicating the number of months the lease is to last for.
public Lease(String tenantName, String tenantPhone, int rentRate, int leasePeriod)
{
this.tenantFullName = tenantName;
this.tenantPhone = tenantPhone;
this.rentRate = rentRate;
if (leasePeriod == 12 || leasePeriod == 6) // Check validity of parameter
this.leasePeriod = leasePeriod;
else // if not valid, set to default of 6 month.
this.leasePeriod = 6;
// Determine (automatically) how many weeks the lease is for, and set as initial value
// for the 'weeksRemaining' field:
if (this.leasePeriod == 12)
this.weeksRemaining = 52; // a Year remains to be paid for
else
this.weeksRemaining = 26; // Only half a year remains to be paid for.
// Prepare the lease for future Maintenance records...
maintenanceRecord = new ArrayList<Maintenance>();
}
// ACCESSORS
public String getTenantFullName()
{
return tenantFullName;
}
public String getTenantPhone()
{
return tenantPhone;
}
public int getRentRate()
{
return rentRate;
}
public int getLeasePeriod()
{
return leasePeriod;
}
public int getWeeksRemaining()
{
return weeksRemaining;
}
// Method to reduce the number of weeks remaining to be paid over the period of the lease.
// If the parameter is in excess of the current number of remaining weeks, the method will
// return false, otherwise it returns true and adjusts the number of remaining weeks.
public boolean reduceWeeksRemaining(int howMuch)
{
boolean result;
if (howMuch > weeksRemaining)
result = false;
else
{
weeksRemaining -= howMuch; // Reduces how many weeks remain unpaid.
result = true;
}
return result;
}
// Method to add another maintenance information record to this lease's list of maintenance records.
public void noteMaintenance(String reason, double cost)
{
Maintenance maintInfo = new Maintenance(reason,cost);
maintenanceRecord.add(maintInfo);
}
// Method to return an Iterator that may be used to cycle over all maintenance records for this lease
public Iterator<Maintenance> getMaintenanceRecords()
{
return maintenanceRecord.iterator();
}
}
MAINTENANCE CODE
package assignment2;
// The Maintenance class is used to represent an individual occurrence of Maintenance
// and records the cost and description of the maintenance.
public class Maintenance {
private String reason;
private double cost;
public Maintenance(String reason, double cost)
{
this.reason = reason;
this.cost = cost;
}
// ACCESSORS:
public String getReason()
{
return reason;
}
public double getCost()
{
return cost;
}
// Gives a explanation of the cost and reason represented by this Maintenance object.
public String toString()
{
return "$" + cost + " for " + reason;
}
}
The reason is that you initialize setupResults() before R.
change:
private JPanel jpButtons = setupButtons(), jpResults = setupResults();
public RealEstateAgency R = new RealEstateAgency();
public RealEstateManage()
{
to
private JPanel jpButtons;
private JPanel jpResults;
public RealEstateAgency R = new RealEstateAgency();
public RealEstateManage()
{
jpButtons = setupButtons();
jpResults = setupResults();
As a sidenote:
You take the result as:
ArrayList<Landlord> Alist = R.getLandlords();
but never use that value.
Maybe you meant to write:
ArrayList<Landlord> Alist = R.getLandlords();
jlLandlord = new JList<Landlord>(Alist.toArray(new Landlord[0]));
^ ^^^^^^^^^^^^^^^^^^^^^^^^^
but you did not.
Can I easily put the entry values of a filtered view into a hashmap?
I have a repeat control, bound to a view, with a dynamic filter.
The user can change the filter by several djFilteringSelect controls, corresponding to the view columns.
Depending on the selection in the 1st djFilteringSelect, the selection in the next djFilteringSelects should be limited to the possible entries ("similar to the data filter in excel"). At the moment I do this with separate #dbcolumn/#dblookup methods for the djFilteringSelects, but I think it is much better and easier to just fill the view entries values into a hashmap and show the hashmap values in the djFilteringSelect.
I found few threads here with repeat controls and hashmaps, but these examples also build the doc collection separatly, which I wish to avoid.
Thanks for any help,
Uwe
There's a reason why all these examples build their document collections separately. Instead of "the view is in the UI", so I must use it, you might have an easier time to build a bean that serves as the source for your repeat control. A bean data source or a managed bean. This will allow for a use case, where you show 2 filter results (e.g England/London and France/Lyon) in one display, something a filtered view can't do.
Update
If you have a lot of reader/author fields, you want to have a view categorized by them, to populate your "backing bean" - there is lot of performance to gain. Holding a few hundred items in memory isn't a big deal.
There are 2 trains of though: make it generic, so every line in the view ends up as a Collection item (Array, List, Map etc.) or to build dedicated line items with clear names. This trains collide quite often, let me take you to the dedicated train for a moment. :-) So your classes (you need 2) would look like this:
package test;
import java.io.Serializable;
import java.util.Vector;
import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.NotesException;
import lotus.domino.ViewEntry;
public class Fruit implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String color;
private String shape;
private String taste;
private String unid = null;
public Fruit() {
// default constructor, might not be needed
}
// To make it easy from a view
public Fruit(final ViewEntry ve) {
try {
#SuppressWarnings("rawtypes")
Vector v = ve.getColumnValues();
// 0 would be the user/group/role
this.setName(v.get(1).toString());
this.setColor(v.get(2).toString());
this.setShape(v.get(3).toString());
this.setTaste(v.get(4).toString());
this.unid = ve.getUniversalID();
} catch (NotesException e) {
e.printStackTrace();
}
}
public void save(Database db) throws NotesException {
Document doc;
if (this.unid == null) {
doc = db.createDocument();
} else {
doc = db.getDocumentByUNID(this.unid);
}
doc.replaceItemValue("Color", this.getColor());
// more here
doc.save();
}
public final String getName() {
return this.name;
}
public final void setName(String name) {
this.name = name;
}
public final String getColor() {
return this.color;
}
public final void setColor(String color) {
this.color = color;
}
public final String getShape() {
return this.shape;
}
public final void setShape(String shape) {
this.shape = shape;
}
public final String getTaste() {
return this.taste;
}
public final void setTaste(String taste) {
this.taste = taste;
}
}
That's to hold a line item (using my favourite fruits example). In your code the repeat control variable (or the data table variable) - instead of the view control, would hold one Fruit instance coming from fruitController.getSelectedFruits() (which you can use in EL as fruitController.selectedFruits), so you can bind your columns using varName.color, varname.shape
The class around it looks roughly like:
package test;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import java.util.TreeSet;
import lotus.domino.Database;
import lotus.domino.NotesException;
import lotus.domino.Session;
import lotus.domino.View;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;
public class FruitController implements Serializable {
private static final long serialVersionUID = 1L;
private static final String FRUIT_VIEW = "(FruitsByUser)";
private final Collection<Fruit> allFruits = new ArrayList<Fruit>();
private final Set<String> colors = new TreeSet<String>();
private final Set<String> shape = new TreeSet<String>();
private final Set<String> taste = new TreeSet<String>();
private String colorFilter = null;
private String tasteFilter = null;
private String shapeFilter = null;
// if you use this controller, you only can use an object data source!
// for a bean you would need an empty controller
public FruitController(final Session s, final Database db) {
this.populateData(s, db);
}
public final String getColorFilter() {
return this.colorFilter;
}
public final String[] getColors() {
return (String[]) this.colors.toArray();
}
public Collection<Fruit> getSelectedFruits() {
Collection<Fruit> result = new ArrayList<Fruit>();
for (Fruit f : this.allFruits) {
if (this.matchesFilter(f)) {
result.add(f);
}
}
return result;
}
public final String[] getShape() {
return (String[]) this.shape.toArray();
}
public final String getShapeFilter() {
return this.shapeFilter;
}
public final String[] getTaste() {
return (String[]) this.taste.toArray();
}
public final String getTasteFilter() {
return this.tasteFilter;
}
public void resetFilters() {
this.setColorFilter(null);
this.setShapeFilter(null);
this.setTasteFilter(null);
}
public final void setColorFilter(String colorFilter) {
this.colorFilter = colorFilter;
}
public final void setShapeFilter(String shapeFilter) {
this.shapeFilter = shapeFilter;
}
public final void setTasteFilter(String tasteFilter) {
this.tasteFilter = tasteFilter;
}
private boolean matchesFilter(Fruit f) {
boolean result = true;
result = ((result == false) ? false : ((this.colorFilter == null || "".equals(this.colorFilter.trim())) ? true
: (this.colorFilter.equals(f.getColor()))));
result = ((result == false) ? false : ((this.tasteFilter == null || "".equals(this.tasteFilter.trim())) ? true
: (this.tasteFilter.equals(f.getTaste()))));
result = ((result == false) ? false : ((this.shapeFilter == null || "".equals(this.shapeFilter.trim())) ? true
: (this.shapeFilter.equals(f.getShape()))));
return result;
}
private void populateData(final Session s, final Database db) {
try {
final View v = db.getView(FRUIT_VIEW);
// You might need to loop a little here to get all the values
final ViewEntryCollection vec = v.getAllEntriesByKey(s.getUserName());
ViewEntry ve = vec.getFirstEntry();
while (ve != null) {
ViewEntry nextVe = vec.getNextEntry(ve);
Fruit f = new Fruit(ve);
this.updateSelectors(f);
this.allFruits.add(f);
ve = nextVe;
nextVe.recycle();
}
vec.recycle();
v.recycle();
} catch (NotesException e) {
// TODO Stacktrace is no error handling
e.printStackTrace();
}
}
private void updateSelectors(Fruit f) {
this.colors.add(f.getColor());
this.shape.add(f.getShape());
this.taste.add(f.getTaste());
}
}
Of course you can make that more sophisticated by filtering the selection values based on the other selections (e.g. after picking a color only offer the shapes that are available in that color). Using the class as object datasource (e.g. fruitController) should be easy. You can bind your dropdowns to fruitController.colorFilter etc. in EL and define the selections in EL as fruitController.Colors.
Update 2
The data source should be an object data source, like this one:
<xp:this.data>
<xe:objectData var="fruitController" ignoreRequestParams="true"
readonly="false" scope="view"
createObject="#{javascript:return new test.FruitController(session, database);}">
</xe:objectData>
</xp:this.data>
For a bean approach you would need to edit the faces-config.xml and change the class to have a parameterless constructor. For the select values you could stick with the toArray() call in your page or better change the class to return an array in the first place. I updated the class above accordingly (so you can still use EL, no need for SSJS).
Now you only need to add a refresh to the repeat in the onChange event of your selects. Since the new values will be send to the object data source (you bound them to colorFilter, shapeFilter, tasteFilter) the refresh will execute #{fruitController.selectedFruits} which delivers the subset back to the panel.
So the concept here is: You fetch all the user data once into the object data source and once that is loaded filter inside that class, no renewed retrieval or lookup required.
Let us know hoe it goes