what data structure to use in my case? - java

A FileManager Class has a static filed to hold a file collection, this collection may contains files or folders or both , a folder may contains files or folders or both, the FileManager Class contains public method for client code to call such as addFile, addFolder, deleteFile, deleteFolder, these method operate on the collection. My question is:
What java data structure is best for this case ?
How to create model class for File and Folder ?
some example will be good.
best regars.
// added # 2011/05/27
thank everybody !
acutaly I am trying to buidl a eclipse-rcp application to manage some jdbc connection profile.
here is my code:
package com.amarsoft.sysconfig.plugin.model;
/**
* #author ggfan#amarsoft
*
*/
public class TreeNode {
/**
* unique key
*/
private String key;
/**
* used as label in a JFace TreeViewer,
*/
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setKey(String key) {
this.key = key;
}
public String getKey() {
return key;
}
}
public class LeafNode extends TreeNode {
private FolderNode parent;
public void setParent(FolderNode parent) {
this.parent = parent;
}
public TreeNode getParent() {
return parent;
}
}
package com.amarsoft.sysconfig.plugin.model;
import java.util.List;
public class FolderNode extends TreeNode {
private List<TreeNode> children;
public void setChildren(List<TreeNode> children) {
this.children = children;
}
public List<TreeNode> getChildren() {
return children;
}
}
package com.amarsoft.sysconfig.plugin.model;
import org.dom4j.Element;
import org.dom4j.tree.DefaultElement;
/**
* 连接配置模型类
* #author ggfan#amarsoft
*
*/
public class ConnectionProfile extends LeafNode{
/**
* url
*/
private String url;
/**
* JDBC driver id
*/
private int driver;
/**
* user name for logon
*/
private String user;
/**
* password for logon
*/
private String pswd;
/**
* default constructor
*/
public ConnectionProfile() {
}
/**
* construct a instance using a XML element
* #param xmlElement the XML element
*/
public ConnectionProfile(Element xmlElement){
this.setName(xmlElement.attributeValue("name"));
this.setUrl(xmlElement.element("url").getTextTrim());
this.setUser(xmlElement.element("user").getTextTrim());
this.setPswd(xmlElement.element("password").getTextTrim());
}
/**
* serialize as XML
* #return
*/
public Element asXML(){
Element e = new DefaultElement("profile");
e.addAttribute("name", this.getName());
e.addElement("url", escapeNull(this.getUrl()));
e.addElement("user", escapeNull(this.getUser()));
e.addElement("password", escapeNull(this.getPswd()));
return e;
}
private String escapeNull(String s) {
return s == null ? "" : s;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPswd() {
return pswd;
}
public void setPswd(String pswd) {
this.pswd = pswd;
}
public void setDriver(int driver) {
this.driver = driver;
}
public int getDriver() {
return driver;
}
}
public class ConnectionProfileManager {
private static List<TreeNode> profiles = new ArrayList<TreeNode>();
public static void loadProfiles() throws DocumentException{
Element profiles = XMLUtil.readRoot(ConnectionProfileManager.class.getResourceAsStream("samples_profile.xml"));
//Element profiles = XMLUtil.readRoot(new File(ApplicationFiles.CONNNECTION_PROFILES));
if(profiles != null){
for(Element profile : profiles.elements()){
loadNode(profile, ConnectionProfileManager.profiles);
}
}
}
private static void loadNode(Element node, List<TreeNode> parent){
if(node.getName().equals(XMLConstants.CP_TAG_PROFILE)){
ConnectionProfile profile = new ConnectionProfile(node);
parent.add(profile);
}else if(node.getName().equals(XMLConstants.CP_TAG_FOLDER)){
FolderNode folder = new FolderNode();
folder.setChildren(new ArrayList<TreeNode>());
folder.setName(node.attributeValue(XMLConstants.CP_ATTR_NAME));
for(Element child : node.elements()){
loadNode(child, folder.getChildren());
}
parent.add(folder);
}
}
public static void saveProfiles(){
Element root = new DefaultElement(XMLConstants.CP_TAG_PROFILES);
for(TreeNode node : ConnectionProfileManager.profiles){
saveNode(node, root);
}
XMLUtil.save(root, new File("c:\\1.xml"));
}
private static void saveNode(TreeNode node, Element root) {
if(node instanceof ConnectionProfile){
ConnectionProfile p = (ConnectionProfile)node;
root.add(p.asXML());
}else if(node instanceof FolderNode){
FolderNode folder = (FolderNode)node;
Element e = new DefaultElement(XMLConstants.CP_TAG_FOLDER);
e.addAttribute(XMLConstants.CP_ATTR_NAME, node.getName());
for(TreeNode child : folder.getChildren()){
saveNode(child, e);
}
root.add(e);
}
}
public static void addProfile(ConnectionProfile profile){
profiles.add(profile);
}
public static void addProfile(TreeNode parentNode, ConnectionProfile profile){
}
public static List<TreeNode> getProfiles() {
return profiles;
}
}
with these class I get my tree works, but I found It's hard to support add operation.

You've kinda of dictacted the answer already in the question..
The File class is (according to the JavaDocs) an:
abstract representation of file and directory pathnames.
So from what you've described:
// A file manager class
class FileManager {
// has a static field to hold a file collection
static Collection<File> fileCollection;
// contains public methods such as
public addFile(File f) { }
public deleteFile(File f) { }
public addFolder(File f) { }
public deleteFolder(File f { }
}
If you have to look at implementing your own version of the File class, then the JavaDocs for that should be a good start to understanding this.
AS to what collection is best for the file collection, I think a Set makes most sense. There's no point having more than one file (e.g. a List and two entries of the same file would be meaningless), and testing membership of a set is a very quick operation. For example, in addFile you might check it exists before trying to add, and similarly for delete you'd want to make sure that it exists before you delete it.
A couple of points about the design you've mentioned.
Static fields like this as nasty. They make it difficult to test and are a pain for multi-threading. Could you make it an instance variable?
Given that File is an abstract representation of a path name, why'd you need the method addFile and addFolder, they are going to have the same implementation?

A Collection of Files?
Most collections support add and delete, so there is no need for a special data structure. Java File can be a file and a directory. Simply call isDirectory to find out if it is a directory or a file.
Unless you have more requirements, I think this would make up a pretty easy to use FileManager:
List<File> fileManager = new ArrayList<File>();

Related

Get reference of the object from a separate class

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:)

JTree expandPath works with File but not with wrapper class

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);
}
}

Using POJOs as model layer in JavaFX application

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.

Generic class uses generic argument

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);
}
}

TreeViewer with checkbox

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.

Categories

Resources