/*
* Copyright (c) 2014-2016 Jan Strauß <jan[at]over9000.eu>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package eu.over9000.skadi.ui;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.Border;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Objects;
import java.util.Random;
public class DebugSortedBug extends Application {
private static final int NUM_ENTRIES = 10;
private final ObservableList<TestEntry> entryList = FXCollections.observableArrayList(c -> new Observable[]{c.nameProperty(), c.onlineProperty()});
public static void main(final String[] args) {
Application.launch(args);
}
@Override
public void start(final Stage stage) throws Exception {
for (int i = 0; i < NUM_ENTRIES; i++) {
final TestEntry entry = new TestEntry("Entry" + i);
entryList.add(entry);
final EntryUpdateService updateService = new EntryUpdateService(entry);
updateService.start();
}
final TableView<TestEntry> table = new TableView<>();
table.setBorder(Border.EMPTY);
table.setPadding(Insets.EMPTY);
final TableColumn<TestEntry, TestEntry.EntryState> onlineColumn = new TableColumn<>("State");
onlineColumn.setCellValueFactory(p -> p.getValue().onlineProperty());
final TableColumn<TestEntry, String> nameColumn = new TableColumn<>("Name");
nameColumn.setCellValueFactory(p -> p.getValue().nameProperty());
table.getColumns().add(onlineColumn);
table.getColumns().add(nameColumn);
table.getSortOrder().add(onlineColumn); // if commented out the bug disappears
table.getSortOrder().add(nameColumn);
final FilteredList<TestEntry> filteredList = entryList.filtered(c -> TestEntry.EntryState.ONLINE == c.getOnline());
final SortedList<TestEntry> sortedList = new SortedList<>(filteredList);
sortedList.comparatorProperty().bind(table.comparatorProperty());
table.setItems(sortedList);
final Scene scene = new Scene(table, 800, 600);
stage.setScene(scene);
stage.show();
}
private static class TestEntry {
private final StringProperty name;
private final ObjectProperty<EntryState> online;
public TestEntry(final String channelName) {
name = new ReadOnlyStringWrapper(channelName);
online = new SimpleObjectProperty<>(EntryState.UNKNOWN);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public EntryState getOnline() {
return online.get();
}
public void setOnline(final EntryState online) {
this.online.set(online);
}
public ObjectProperty<EntryState> onlineProperty() {
return online;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final TestEntry that = (TestEntry) o;
return Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
public enum EntryState {UNKNOWN, OFFLINE, ONLINE}
}
private class EntryUpdateService extends ScheduledService<Void> {
private final Random RND = new Random();
public EntryUpdateService(final TestEntry toUpdate) {
setPeriod(Duration.seconds(1));
setOnSucceeded(event -> {
final TestEntry.EntryState newState = TestEntry.EntryState.values()[RND.nextInt(TestEntry.EntryState.values().length)];
toUpdate.setOnline(newState);
});
}
@Override
protected Task<Void> createTask() {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
Thread.sleep(RND.nextInt(100)); // simulate REST request
return null;
}
};
}
}
}