Currently, there are 3 classes which are not inheriting to each other. Each class has a property in it that references to an instance of another class as below.
import java.util.ArrayList;
class Region {
private Directory areaDirectory;
public Region() {
areaDirectory = new Directory();
}
public Directory getAreaDirectory() {
return areaDirectory;
}
public void setAreaDirectory(Directory areaDirectory) {
this.areaDirectory = areaDirectory;
}
}
class Directory {
private ArrayList<Area> areaList;
public Directory() {
areaList = new ArrayList<>();
}
public ArrayList<Area> getAreaList() {
return areaList;
}
public void setAreaList(ArrayList<Area> areaList) {
this.areaList = areaList;
}
public Area addNewArea(){
Area area = new Area();
return area;
}
}
class Area {
private String Name;
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public Region getAreaRegion() {
// This returns a new region but need the region it was created in
return new Region();
}
}
public class Scratch {
public static void main(String args[]) {
Region r = new Region();
Area a = r.getAreaDirectory().addNewArea();
a.setName("Demo");
//See getAreaRegion() method in Area class
System.out.println(a.getAreaRegion());
}
}
How to implement a method like getAreaRegion() such that it returns the region object r? How to setup inheritance in this example to get parent objects?
I have understood the business logic which you want to describe with codes as below:
There is a region. Every region has a directory. And every directory has an area.
In this case I think it would be nice if :
Region class holds reference to Directory field;
And Directory class also has reference for its own Region;
Directory class holds reference to Area field;
And Area class also has reference for its own Directory.
Then we can create classes in following way:
1.Region class
public class Region {
private Directory directory;
public Directory getDirectory() {
return directory;
}
public void setDirectory(Directory directory) {
this.directory = directory;
}
}
2.Directory class
class Directory{
private Region region;
private Area area;
public Region getRegion() {
return region;
}
public void setRegion(Region region) {
this.region = region;
}
public Area getArea() {
return area;
}
public void setArea(Area area) {
this.area = area;
}
}
3.Area class
class Area{
private Directory directory;
private String Name;
public Directory getDirectory() {
return directory;
}
public void setDirectory(Directory directory) {
this.directory = directory;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
}
So we can get region of area as following:
area.getDirectory().getRegion();
I think this shows business logic better and simply. Hope it would be helpful for someone:)
I wonder what the requirements are so that the JTree properly works. I have written some code, displaying a filesystem tree and then call expandPath() to expand a path on it.
When I use a File object everything works fine, but the tree shows the full path on each node. So I wrapped it in a class which changes the toString() so that only the dir/filename is shown, but now expandPath() no longer works and the path is not expanded.
package tools.controls.TreeControl.Filesystem;
import java.io.File;
import java.net.URI;
public class TreeFile
extends File
implements Cloneable
, Comparable<File>
{
private static final long serialVersionUID = 1L;
public TreeFile(TreeFile oOther)
{
super(oOther.getParentFile(), oOther.getName());
}
public TreeFile(File oFile)
{
super(oFile.getParentFile(), oFile.getName());
}
public TreeFile(String oPath)
{
super(oPath);
}
public TreeFile(URI oFileURL)
{
super(oFileURL);
}
public TreeFile(String oParent, String oChild)
{
super(oParent, oChild);
}
public TreeFile(File oParent, String oChild)
{
super(oParent, oChild);
}
public TreeFile getParentTreeFile()
{
File f = getParentFile();
if(f == null)
return null;
return new TreeFile(f);
}
public String toString()
{
return getName();
}
public TreeFile clone()
{
return new TreeFile(this);
}
#Override
public int compareTo(File oObject)
{
return super.compareTo(oObject);
}
}
I'm creating simple JavaFX application. I want my model layer to be completely independent from JavaFX - no StringProperty, IntegerProperty and etc. as fields. I want it to be POJO. Main reason to do so is that I want it to be Serializable.
I've created DataRepository - simple CRUD-like interface and some implementations of it, so I can at anytime change where I store my data - XML file, SQLite database or anything else. I also have to somehow connect my data storage with JavaFX (to display its content in TableView), so I decided to create my implementation of ObservableList which wraps my repository. My question is - is there any other way? ObservableList contains about 30 methods to implement and it looks like I'm doing something wrong.
My (simplified) model:
public class Movie implements Serializable {
private String title;
private String director;
public Movie() {
}
public Movie(String title, String director) {
this.title = title;
this.director = director;
}
// Getters and setters, equals etc...
}
MovieRepository:
public interface MovieRepository {
public void add(Movie movie);
public void remove(String title);
public void remove(int index);
public Movie get(String title);
public Movie get(int index);
public List<Movie> getAll();
}
Controller for my main view:
public class MainController {
#FXML
private TableView<Movie> movieTable;
#FXML
private TableColumn<Movie, String> movieTitleColumn;
#FXML
private Label titleLabel;
private MovieRepository movies = new DBMovieRepository(); //MovieRepository implementation which uses SQLite DB to store data
private MainApp app;
#FXML
private void initialize() {
movieTable.setItems(new ObservableMovies(movies));
// ObservableMovies is my implementation of ObservableList
// It basically wraps methods from MovieRepository
// and notifies listeners
showMovieDetails(null);
movieTitleColumn.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue().getTitle()));
movieTable.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> showMovieDetails(newValue));
}
private void showMovieDetails(Movie movie) {
if(movie != null) {
titleLabel.setText(movie.getTitle());
} else {
titleLabel.setText("");
}
}
#FXML
private void handleNew() {
Movie movie = new Movie();
app.showNewMovieDialog(movie);
movieTable.getItems().add(movie);
}
public void setApp(MainApp app) {
this.app = app;
}
}
You have a couple of options here (maybe more), which are covered in other questions on this site. However, for convenience, I'll summarize them here too.
1. Use JavaFX Properties and make the class Serializable
You can do this with a custom serialized form. Make the JavaFX properties transient and implement readObject and writeObject to store the values they wrap:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Objects;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Movie implements Serializable {
private transient StringProperty title = new SimpleStringProperty();
private transient StringProperty director = new SimpleStringProperty();
public Movie() {
}
public Movie(String title, String director) {
setTitle(title);
setDirector(director);
}
#Override
public int hashCode() {
return Objects.hash(getDirector(), getTitle());
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Movie other = (Movie) obj;
return Objects.equals(getTitle(), other.getTitle())
&& Objects.equals(getDirector(), other.getDirector());
}
public final StringProperty titleProperty() {
return this.title;
}
public final String getTitle() {
return this.titleProperty().get();
}
public final void setTitle(final String title) {
this.titleProperty().set(title);
}
public final StringProperty directorProperty() {
return this.director;
}
public final String getDirector() {
return this.directorProperty().get();
}
public final void setDirector(final String director) {
this.directorProperty().set(director);
}
private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
s.defaultReadObject();
title = new SimpleStringProperty((String) s.readObject());
director = new SimpleStringProperty((String) s.readObject());
}
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeObject(getTitle());
s.writeObject(getDirector());
}
}
2. Use a POJO with "bound properties".
See JavaBean wrapping with JavaFX Properties for details. In brief:
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class Movie {
private String title ;
private String director ;
private final PropertyChangeSupport propertySupport ;
public Movie(String title, String director) {
this.title = title ;
this.director = director ;
this.propertySupport = new PropertyChangeSupport(this);
}
public Movie() {
this("", "");
}
public String getTitle() {
return title ;
}
public String setTitle(String title) {
String oldTitle = this.title ;
this.title = title ;
propertySupport.firePropertyChange("title", oldTitle, title);
}
// similarly for director...
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertySupport.addPropertyChangeListener(listener);
}
// hashCode and equals...
}
For wanting to wrap your repository as an observable list, instead wrap it with a repository implementation that uses an observable list:
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class ObservableMovieRepository implements MovieRepository {
private final MovieRepository repository ;
private final ObservableList<Movie> movieList;
public ObservableMovieRepository(MovieRepository repository) {
this.repository = repository ;
this.movieList = FXCollections.observableArrayList(repository.getAll());
}
#Override
public void add(Movie movie) {
repository.add(movie);
movieList.add(movie);
}
#Override
public void remove(String title) {
Movie movie = get(title);
repository.remove(title);
movieList.remove(title);
}
#Override
public void remove(int index) {
repository.remove(index);
movieList.remove(index);
}
#Override
public Movie get(String title) {
return repository.get(title);
}
#Override
public Movie get(int index) {
return movieList.get(index);
}
#Override
public ObservableList<Movie> getAll() {
return movieList ;
}
}
This uses the standard ObservableList implementation that copies an existing list on creation, and the implementation keeps that list in sync with the list in the wrapped repository. Now your UI code can do
ObservableMovieRepository movies = new ObservableMovieRepository(new DBMovieRepository());
// ...
movieTable.setItems(movies.getAll());
With the Movie class above, you would just do
movieTitleColumn.setCellValueFactory(cellData -> cellData.getValue().titleProperty());
If you use the POJO version you can do
movieTitleColumn.setCellValueFactory(cellData -> {
try {
return new JavaBeanStringPropertyBuilder()
.bean(cellData.getValue())
.name("title")
.build();
} catch (Exception e) { throw new RuntimeException(e); }
}
There seem to be multiple question in here, so I'm not really sure, if I understood you correctly, but I will try to split it up a bit.
I want my model layer to be completely independent from JavaFX - no
StringProperty, IntegerProperty and etc. as fields. I want it to be
POJO.
You could mark your properties as transient. Then you just need to wrap them around your values and it will be both JavaFX compliant and Serializable. You just have to propagate changes back to your backing attributes.
I also have to somehow connect my data storage with JavaFX (to display
its content in TableView), so I decided to create my implementation of
ObservableList which wraps my repository. My question is - is there
any other way?
Very limited information on this and I really don't know, why you would need to create your own implementation of ObservableList, but to keep it POJO, you could maintain plain java.util.Collections in your bean and provide transient ObservableLists, which you can create on creation by wrapping your java.util.Lists in your POJO. You can find those methods in the FXCollections utility class.
ObservableList contains about 30 methods to implement and it looks
like I'm doing something wrong.
If you really need to implement it, you can inherit from ObservableListBase.
Situation
I am making a graph class that looks like this:
class ImmutableGraph<G> {
Node<G> selectedNode;
private ImmutableGraph(Node<G> initialNode) { selectedNode = initialNode; }
//many more things
}
and I'm currently using a (nested) builder class like so
public static class GraphBuilder<B> {
Node<B> currentNode;
public GraphBuilder(B value){ currentNode = new Node(value); }
public ImmutableGraph<B> build(){
return new ImmutableGraph<B>(currentNode);
}
//many more things
}
which uses the (nested) node class
private static class Node<N> {
private final N value;
Array<Nodes<N>> neighbours;
public Node(N v){ value = v; }
//many more things
}
Problem
I can't find a way to instantiate my ImmutableGraph using my builder because the return type is not correct. Indeed, compilation suggests that GraphBuilder.build() should return a type ImmutableGraph<Node<B>> and not ImmutableGraph<B>
For now the only solution I found is to change the return type to ImmutableGraph<Node<B>> but that's feels dumb since all graphs (except empty ones) are graphs of nodes. The Node type is also confusing since the user never interacts with it.
edit:
corrected the "new" in the factory method of the builder
I think that your build method should return new ImmutableGraph<B>(currentNode);
import java.util.List;
public class ImmutableGraph<G> {
Node<G> selectedNode;
private ImmutableGraph(Node<G> initialNode) {
selectedNode = initialNode;
}
// many more things
public static class GraphBuilder<B> {
Node<B> currentNode;
public GraphBuilder(B value) {
currentNode = new Node<B>(value);
}
public ImmutableGraph<B> build() {
return new ImmutableGraph<B>(currentNode);
}
// many more things
}
private static class Node<N> {
private final N value;
List<Node<N>> neighbours;
public Node(N v) {
value = v;
}
// many more things
}
public static void main(String[] args) {
GraphBuilder<Integer> builder = new GraphBuilder<Integer>(Integer.MAX_VALUE);
ImmutableGraph<Integer> graph = builder.build();
System.out.println(graph.selectedNode.value);
}
}
I am currently creatin a TreeView where leaf elements should be checkable.
I created the sampleTreeView from the eclipse plugin that comes with a predefined Tree structure.
public class TreeObject {
private String name;
private TreeParent parent;
public TreeObject(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setParent(TreeParent parent) {
this.parent = parent;
}
public TreeParent getParent() {
return parent;
}
public String toString() {
return getName();
}
public Object getAdapter(Class<?> key) {
return null;
}
}
public class TreeParent extends TreeObject{
private ArrayList<TreeObject> children;
public TreeParent(String name) {
super(name);
children = new ArrayList<TreeObject>();
}
public void addChild(TreeObject child) {
children.add(child);
child.setParent(this);
}
public void removeChild(TreeObject child) {
children.remove(child);
child.setParent(null);
}
public TreeObject [] getChildren() {
return (TreeObject [])children.toArray(new TreeObject[children.size()]);
}
public boolean hasChildren() {
return children.size()>0;
}
}
I then found the following tutorial. He is using the TreeItem Object where it is easy to attach a Image. Can I somehow Copy this function or do I have to use the TreeItem object as my data structure?
The tutorial says at the beginning of part 2 that you should use a ContentProvider and LabelProvider for the TreeViewer rather than use TreeItem, and that is what you should do.
The getImage method of the label provider would return the checked / unchecked /null image.
When you need to change an image call TreeViewer.update or TreeViewer.refresh if the children of the object also need refreshing. This will call the label provider again.