Randomly Generate a Tree Structure Consisting of Objects with their Parents - java

I'm trying to randomly generate a tree structure of objects that look something like the following:
Branch parent = new Branch("Start");
Branch branch1 = new Branch(parent, "Branch 1");
Branch branch2 = new Branch(parent, "Branch 2");
Branch branch21 = new Branch(branch2, "Branch 2.1");
Branch branch22 = new Branch(branch2, "Branch 2.2");
I know how to create objects manually, and how to generate random numbers, and I've seen some stuff on randomly generating nodes for creating fractals, but I'm pretty lost all the sudden, as I've never had to programmatically generate objects before.
Any ideas as to where to start, or what technique to use would be appreciated.

Typically, when building such items it's good to have a designed in and out. Otherwise, you will find yourself having to support many methods of application to support the intended result.
Hope this helps!
Try something along the following :)
Branch start = new Branch();
CreateChildren(start);
const int ChildrenLimitCheck = 0;
private void CreateChildren(Branch parent) {
//Use a limit variable so that you can decrease, and if it's equal to a sepcific number(usually 0) exit.
int Limit = (int) (Math.random() * 5);
//Call the function that's recursive, inside of a function that isn't recursive. This gives you a clean way to interface with the recursive function without excessive lines of code in other areas
generateChildren(parent,Limit);
}
private void generateChildren(Branch parent,int limit) {
//Check to see if we've hit our minimum. If so, exit out of the logic
if(limit == ChildrenLimitCheck)
return;
//Specify the random number of branches created in this instance
int numberOfBranches = (int) (Math.random() * 5);
for (int i = 0; i < numberOfBranches; i++) {
Branch child = new Branch(parent);
parent.Children.Add(child);
//Depending on what you want to do, either pass i or pass limit. If you don't use limit, you can remove it from this function :)
//If you pass i, try doing:
//for (int i = numberOfBranches; i > 0; i--)
//So that you can eventually get down to 0, to automatically stop your recursive calls with the above return statement.
//Seems you just want to run a loop for xxx number of times. This will still grant that, but you won't have to guess the upper limit
//of numberOfBranches to exit on, and you'll be able to exit cleanly
//This may be what caused your stackoverflow error. For all recursive functions, you need an exit condition or it will run indefinately
generateChildren(child,--limit);
//generateChildren(child,i);
}
}

I do this quite often and I can definately help in some areas of efficiency.
Try logic similar to the following. Place a breakpoint at Console.Write to see depth. This is a C++ object oriented approach, which I personally prefer over LINQ for performance. I didn't fine tune this, so LINQ may be faster. But if you know what you're doing, you can significantly increase performance via custom methods and inheritance.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication11
{
public class Branch
{
//Branch Name
public string BranchName { get; set; }
//Add depth
public List<Branch> Children = new List<Branch>();
//Add a parent
public Branch Parent = null;
public Branch()
{
}
public Branch(string BranchName)
{
this.BranchName = BranchName;
}
public Branch(Branch Parent, string BranchName)
{
this.Parent = Parent;
this.BranchName = BranchName;
}
//Internal Functions
public bool HasParent()
{
return this.Parent != null;
}
public Branch getParent()
{
return this.Parent;
}
}
//Wrap a list inside of a class so we can have leverage over the add, and create our own functions for our need
public class BranchList
{
public List<Branch> branchParentList = new List<Branch>();
public void AddBranchToParent(Branch parent, Branch child)
{
foreach (Branch node in branchParentList)
{
if (node == parent)
{
node.Children.Add(child);
}
}
}
public void AddBranchAsChild(string parent, Branch child)
{
foreach (Branch node in branchParentList)
{
if (node.BranchName == parent)
{
node.Children.Add(child);
return;//Exit out, don't do the other loop. We found it
}
//Depth
AddBranchAsChildInChildren(node, child,parent);
}
}
public void AddBranchAsChildInChildren(Branch branch,Branch Child,string parent)
{
foreach(Branch child in branch.Children)
{
if (child.BranchName == parent)
child.Children.Add(Child);
}
}
public void AddBranchAsChildInChildren(Branch branch, string Child, string parent)
{
foreach (Branch child in branch.Children)
{
if (child.BranchName == parent)
child.Children.Add(new Branch() { BranchName=Child });
}
}
public void AddBranchAsChild(string parent, string child)
{
foreach (Branch node in branchParentList)
{
if (node.BranchName == parent)
{
node.Children.Add(new Branch() { BranchName = child });
return;
}
//Depth
AddBranchAsChildInChildren(node, child, parent);
}
}
public void AddBranchAsParent(Branch Branch, Branch Child)
{
if (branchParentList.Contains(Branch) == false)
throw new Exception("Parent exists");
foreach (Branch b in branchParentList)
{
if (b == Child)
{
b.Parent = Branch;
}
}
}
public void AddParent(Branch Parent)
{
if (branchParentList.Contains(Parent))
throw new Exception("Parent exists");
this.branchParentList.Add(Parent);
}
}
//Wrap the list, use it as an interface
public class BranchManager
{
public BranchList branchList = new BranchList();
public BranchManager()
{
}
public void AddParent(Branch Branch)
{
branchList.AddParent(Branch);
}
public void AddBranchAsChild(Branch Parent, Branch Child)
{
branchList.AddBranchToParent(Parent, Child);
}
public void AddBranchAsChild(string ParentName, Branch Child)
{
branchList.AddBranchAsChild(ParentName, Child);
}
public void AddBranchAsChild(string ParentName, string ChildName)
{
branchList.AddBranchAsChild(ParentName, ChildName);
}
}
class Program
{
static void Main(string[] args)
{
/*
Branch parent = new Branch("Start");
Branch branch1 = new Branch(parent, "Branch 1");
Branch branch2 = new Branch(parent, "Branch 2");
Branch branch21 = new Branch(branch2, "Branch 2.1");
Branch branch22 = new Branch(branch2, "Branch 2.2");
*/
BranchManager branchManager = new BranchManager();
branchManager.AddParent(new Branch("Start"));
branchManager.AddBranchAsChild("Start", "Branch 1");
branchManager.AddBranchAsChild("Start", "Branch 2");
branchManager.AddBranchAsChild("Branch 2", "Branch 2.1");
branchManager.AddBranchAsChild("Branch 2", "Branch 2.2");
Console.WriteLine(branchManager.branchList.branchParentList.Count);
}
}
}

Best guess so far, using a recursive function to handle the randomly generating children & parents:
Branch start = new Branch();
generateChildren(start);
private void generateChildren(Branch parent) {
int numberOfBranches = (int) (Math.random() * 5);
for (int i = 0; i < numberOfBranches; i++) {
Branch child = new Branch(parent);
generateChildren(child);
}
}
It properly creates a list of Branch objects with linked parents. I ran into a StackOverflow error until I wrapped it in a counter that limits the total number of branches.
The real question is: Is there a non-recursive way to handle it?

Related

Vaadin 10+ Tree Grid: Hierarchies and how they work?

I have been trying to do a Vaadin tree grid with a more complex hierarchy. Since the documentation for this framework is very obscure and feels tad incomplete, I found this blog thread that helped me out only to get multiple children to a single parent.
However, I want to have a much more complex system where the children would have their own branches (children of children?).
For example:
So now I am stuck with this bit of code and I am unsure if I am going the right way or not.
TreeGrid<TreeDto> grid = new TreeGrid<>(TreeDto.class);
grid.setHierarchyColumn("name");
List<TreeDto> parenlist = new ArrayList<TreeDto>();
List<TreeDto> childList = new ArrayList<TreeDto>();
for(DataDepartment department : departmentLists)
{
TreeDto parent = new TreeDto(department.getDepName(), null);
for(DataGeneralSection section: sectionList)
{
childList.add(new TreeDto(section.getSection(), parent));
}
parenlist.add(parent);
}
List<TreeDto> newList = new ArrayList<TreeDto>(parenlist);
newList.addAll(childList);
newList.forEach(p -> grid.getTreeData().addItem(p.getParent(), p));
abteilungenTabs.add(grid);
Does anybody have any idea how I can achieve the hierarchy I want?
You can just continue deeper with your current approach. If you have a lot of items, you might want to consider a data provider like Simon Martinelli suggested.
public class MainView extends VerticalLayout {
public MainView() {
TreeGrid<TreeItem> treeGrid = new TreeGrid<>();
treeGrid.addHierarchyColumn(TreeItem::getValue);
setSizeFull();
treeGrid.setSizeFull();
for (Company company: getCompanies()) {
TreeItem companyItem = new TreeItem(company.getCompanyName());
treeGrid.getTreeData().addItem(null, companyItem);
for(Department department: company.getDepartments()) {
TreeItem departmentItem = new TreeItem(department.getName());
treeGrid.getTreeData().addItem(companyItem, departmentItem);
for(Employee employee: department.getEmployees()) {
TreeItem employeeItem = new TreeItem(employee.getFullName());
treeGrid.getTreeData().addItem(departmentItem, employeeItem);
}
}
}
add(treeGrid);
}
}
You must use a HierachicalDataProvider that provides the data.
For example:
dataProvider = new AbstractBackEndHierarchicalDataProvider<>() {
#Override
public int getChildCount(HierarchicalQuery<TreeNode, Void> hierarchicalQuery) {
if (hierarchicalQuery.getParent() == null) {
if (root == null) {
return 0;
} else {
return root.getChildren().size();
}
} else {
return hierarchicalQuery.getParent().getChildren().size();
}
}
#Override
public boolean hasChildren(TreeNode treeNode) {
return !treeNode.getChildren().isEmpty();
}
#Override
protected Stream<TreeNode> fetchChildrenFromBackEnd(HierarchicalQuery<TreeNode, Void> hierarchicalQuery) {
if (hierarchicalQuery.getParent() == null) {
if (root == null) {
return Stream.empty();
} else {
return root.getChildren().stream().skip(hierarchicalQuery.getOffset()).limit(hierarchicalQuery.getLimit());
}
} else {
return hierarchicalQuery.getParent().getChildren().stream().skip(hierarchicalQuery.getOffset()).limit(hierarchicalQuery.getLimit());
}
}
};
treeGrid.setDataProvider(dataProvider);

java.lang.InstantiationError. Class becomes abstract

I am currently working on a Bukkit plugin for Minecraft and I am getting a java.lang.InstantiationError error while trying to create a new object. The stacktrace is as follows;
Caused by: java.lang.InstantiationError: fancyui.craterhater.components.FancyComponent
at fancyui.craterhater.uibuilder.ComponentMaker.getComponent(ComponentMaker.java:13) ~[?:?]
at fancyui.craterhater.uibuilder.Editor_Page_0.getComponents(Editor_Page_0.java:40) ~[?:?]
at fancyui.craterhater.uibuilder.Editor$1.call(Editor.java:36) ~[?:?]
at fancyui.craterhater.masterfancyui.FancyUI.openInventory(FancyUI.java:195) ~[?:?]
at fancyui.craterhater.uibuilder.Editor.createEditorGUI(Editor.java:236) ~[?:?]
at fancyui.craterhater.commandhandler.MasterCommand$2.call(MasterCommand.java:69) ~[?:?]
at fancyui.craterhater.commandhandler.CECommand.playerExecutes(CECommand.java:105) ~[?:?]
at fancyui.craterhater.commandhandler.CECommand.checkPlayerExecutes(CECommand.java:98) ~[?:?]
at fancyui.craterhater.commandhandler.MasterCommand.onCommand(MasterCommand.java:153) ~[?:?]
at org.bukkit.command.PluginCommand.execute(PluginCommand.java:44) ~[1.8.jar:git-Spigot-870264a-0a645a2]
... 15 more
Line 13 of the ComponentMaker class simply says;
FancyComponent fancyComponent = new FancyComponent();
I did some debugging and decided to print the following;
System.out.println(FancyComponent.class.toGenericString());
This printed;
public abstract class fancyui.craterhater.components.FancyComponent
While the FancyComponent class is not abstract. Here is the FancyComponent class:
public class FancyComponent {
//FancyComponent class represents the individual items placed in a FancyUI.
//It handles things such as animation, itemstack building and event handling whilst
//allowing the FancyUI to handle loading and deleting of these components.
//FancyComponents are added to the FancyUI by the user of the API using LoadFunctions.
//They can also be generated from a FancyUI file.
private FancyLocation fancyLocation;
private FancyItemStack fancyItemStack;
private FancyDisplayType fancyDisplayType;
private String identifier = null;
private int ID = 0;
private int basicSlot = 0;
private boolean copied;
public FancyComponent() {
fancyDisplayType = FancyDisplayType.DYNAMIC;
}
public FancyComponent copy(FancyLocation newLocation) {
FancyComponent newComponent = new FancyComponent();
newComponent.setDisplayType(fancyDisplayType);
newComponent.setID(ID);
newComponent.setFancyItemStack(fancyItemStack);
newComponent.setFancyLocation(newLocation);
newComponent.setAttributes(attributes);
newComponent.setIdentifier(identifier);
return newComponent;
}
//Method gets called when FancyUI decides to load in new FancyComponents.
//This happens when openInventory gets called and new components are generated.
//This method should start future animations. It should also handle ItemStack placement.
//The boolean here represents whether or not it has succesfully been added. This is used in the
//FancyUI in order to keep track of all active components and not the once off-screen.
public boolean addToUI(Inventory inventory, Player p, FileConfiguration fc, FileConfiguration fc2) {
if(fancyLocation == null) {return false;}
return addToUI(inventory, p, fancyLocation, fc, fc2);
}
public boolean addToUI(Inventory inventory, Player p, FancyLocation fancyLocation, FileConfiguration fc, FileConfiguration fc2) {
if(fancyItemStack == null) {return false;}
FancyUI fancyUI = fancyLocation.getFancyUI();
FancyLocation masterFancyLocation = fancyUI.getFancyData().getCurrentFancyLocation();
if(!fancyLocation.isInView(masterFancyLocation, fancyDisplayType)) {return false;}
int basicSlot = fancyLocation.getBasicLocation(masterFancyLocation.getX(), masterFancyLocation.getY(), fancyDisplayType);
if(basicSlot < 0 || basicSlot >= inventory.getSize()) {return false;}
this.basicSlot = basicSlot;
ItemStack itemStack = fancyItemStack.buildItemStack(p,fc2);
if(fc != null) {
if(fc.contains("Components."+identifier+".Animations")) {
for(String animation : fc.getConfigurationSection("Components."+identifier+".Animations").getKeys(false)) {
if(fc.getBoolean(("Components."+identifier+".Animations."+animation+".continious"))){
List<String> frames = new ArrayList<>();
if(fc.contains("Components."+identifier+".Animations."+animation+".animation")) {
frames = fc.getStringList("Components."+identifier+".Animations."+animation+".animation");
}
int interval = fc.getInt("Components."+identifier+".Animations."+animation+".interval");
if(interval < 1) {
interval = 2;
}
int position = fc.getInt("Components."+identifier+".Animations."+animation+".position");
boolean continious = fc.getBoolean("Components."+identifier+".Animations."+animation+".continious");
boolean reverse = fc.getBoolean("Components."+identifier+".Animations."+animation+".reverse");
fancyItemStack.startAnimation(frames, p, interval, position, this, continious, reverse);
break;
}
}
}
}
if(fancyLocation.getFancyUI().getFancyData().isPlayerInventory()) {
if(fancyItemStack.getCustomName().getFancyStringAnimations().length > 0) {
fancyItemStack.startAnimation(fancyItemStack.getCustomName().getFancyStringAnimations()[0].getFrames(), p, 4, 0, this, true, false);
}
}
inventory.setItem(basicSlot, itemStack);
return true;
}
//Method to reload just this component. It is used when cycling through fancyLore. If a function makes changes
//to other components the reload method in the FancyUI has to be called in order to fully reload a page.
public void reload(Player p) {
FileConfiguration fc = DataHandler.getFile(96,p.getUniqueId().toString(), false, "Data", "Users");
ItemStack itemStack = fancyItemStack.buildItemStack(p,fc);
if(fancyLocation == null) {
return;
}
if(fancyLocation.getFancyUI().getFancyData().isPlayerInventory()) {
p.getInventory().setItem(basicSlot, itemStack);
return;
}
fancyLocation.getFancyUI().getFancyData().getInventory().setItem(basicSlot, itemStack);
}
//Method gets called when FancyUI deletes all information on other FancyComponents.
//This happens when openInventory gets called and new components are generated.
//Make sure that this method cleans up future animations so they don't go around throwing nullpointers.
//The ItemStack itself is removed automatically by the FancyUI.
public void notifyOfDeletion() {
fancyItemStack.stopAnimating();
}
//Attributes are pieces of code written by the user of the API in order to have certain things
//happen at certain events such as ON_CLICK or ON_DROP etc.
private List<Attribute> attributes = new ArrayList<>();
public void addAttribute(Attribute attribute) {
attributes.add(attribute);
}
public List<Attribute> getAttributes(){
return attributes;
}
public void setAttributes(List<Attribute> attributes) {
this.attributes = attributes;
}
//Method to identify which Attributes need to run at these specific occasions. These occasions
//may include things suchs as ON_CLICK or ON_1 just to name a few. This method consists of two
//distinct parts. The first part handles all generated components which have scripts assigned to them
//and the second part handles all components that have been generated by the plugin itself. And thus
//do not have scripts assigned to them.
public void handleEvent(FileConfiguration fc, FileConfiguration fc2, FunctionParams params, AttributeOccasion... attributeOccasions) {
//The 'identifier' is a tag all generated components get. It is unique to all components.
//Non-generated components do not have this tag and thus we can check for null to see whether or not
//it is generated.
if(identifier != null) {
if(fc.contains("Components."+identifier+".Scripts")) {
for(String script : fc.getConfigurationSection("Components."+identifier+".Scripts").getKeys(false)) {
if(fc.contains("Components."+identifier+".Scripts."+script+".events")) {
List<String> scriptLines = fc.getStringList("Components."+identifier+".Scripts."+script+".script");
for(String event : fc.getStringList("Components."+identifier+".Scripts."+script+".events")) {
A: for(AttributeOccasion attributeOccasion : attributeOccasions) {
if(attributeOccasion.name().equalsIgnoreCase(event)) {
fancyLocation.getFancyUI().getScriptParser().executeScript(params.getPlayer(), scriptLines, FancyUI.fancyUI.get(params.getPlayer().getUniqueId()), params.getFancyComponent(), fc2, false);
break A;
}
}
}
}
}
}
if(fc.contains("Components."+identifier+".Animations")) {
for(String script : fc.getConfigurationSection("Components."+identifier+".Animations").getKeys(false)) {
if(fc.contains("Components."+identifier+".Animations."+script+".events")) {
for(String event : fc.getStringList("Components."+identifier+".Animations."+script+".events")) {
A: for(AttributeOccasion attributeOccasion : attributeOccasions) {
if(attributeOccasion.name().equalsIgnoreCase(event)) {
List<String> frames = new ArrayList<>();
if(fc.contains("Components."+identifier+".Animations."+script+".animation")) {
frames = fc.getStringList("Components."+identifier+".Animations."+script+".animation");
}
int interval = fc.getInt("Components."+identifier+".Animations."+script+".interval");
if(interval < 1) {
interval = 2;
}
int position = fc.getInt("Components."+identifier+".Animations."+script+".position");
boolean continious = fc.getBoolean("Components."+identifier+".Animations."+script+".continious");
boolean reverse = fc.getBoolean("Components."+identifier+".Animations."+script+".reverse");
fancyItemStack.startAnimation(frames, params.getPlayer(), interval, position, this, continious, reverse);
break A;
}
}
}
}
}
}
}
//Handles all components generated by the plugin as well as generated components from file if this component is inside of an editor.
//If inside an editor generated components will have some attributes linked to them by the plugin. If not inside an editor, generated components
//will not have any attributes linked to them.
A:for(Attribute attribute : attributes) {
for(AttributeOccasion attributeOccasion : attribute.getAttributeOccasions()){
for(AttributeOccasion attributeOccasion2 : attributeOccasions) {
if(attributeOccasion.equals(attributeOccasion2)) {
attribute.getAttributeFunction().call(params);
continue A;
}
}
}
}
}
//This method is used to move the cursor in list cycleables.
public void performCursorMove(int delta, Player p) {
if(fancyItemStack == null) {return;}
FancyLore fancyLore = fancyItemStack.getFancyLore();
if(fancyLore == null) {return;}
fancyLore.moveCursor(delta, p);
}
public void setCursorPosition(int n, Player p) {
if(fancyItemStack == null) {return;}
FancyLore fancyLore = fancyItemStack.getFancyLore();
if(fancyLore == null) {return;}
fancyLore.setCursorPosition(n, p);
}
public int getSlot() {
return basicSlot;
}
public FancyLocation getFancyLocation() {
return fancyLocation;
}
public FancyItemStack getFancyItemStack() {
return fancyItemStack;
}
public void setFancyItemStack(FancyItemStack fancyItemStack) {
this.fancyItemStack = fancyItemStack;
}
public void setFancyLocation(FancyLocation fancyLocation) {
this.fancyLocation = fancyLocation;
}
public void setDisplayType(FancyDisplayType fancyDisplayType) {
this.fancyDisplayType = fancyDisplayType;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public String getIdentifier() {
return identifier;
}
public void setIsCopied(boolean copied) {
this.copied = copied;
}
public boolean isCopied() {
return copied;
}
public void setID(int ID) {
this.ID = ID;
}
public int getID() {
return ID;
}
public FancyDisplayType getFancyDisplayType() {
return fancyDisplayType;
}
}
The weird thing is that this does not occur on later versions of Spigot. I have tested this on 1.8, 1.9, and 1.15 and the error occurs on 1.8 and 1.9 but not on 1.15. Any help would be appreciated.
It appears to only not throw the error on the latest version (1.15). All other versions do throw the error.
After a few hours of testing I decided to copy paste the code of the problematic class into a new class. I renamed all references to this class. This fixed my issue but I am not particularly happy with it especially because the original name is still causing the error. So if I rename the new class to the old name the error reappears.

how to let tableRow flash even if it is not visible? (JavaFx)

At first my question is how to let a newly added row flash in JavaFx, then I went through a lot of questions related to this topic (such as javafx: table row flashing). Most of them are using setRowFactory and override the updateItem method by adding a Timeline animation which change the state of pseudoClass of the row.
Below is my code, I am trying to building a FlashControl which can be reused.
public class TableFlashControl<T> {
private PseudoClass flashHighlight = PseudoClass.getPseudoClass("flash-highlight");
private List<T> newAdded = new ArrayList<>();
private boolean isFilterApplied = false;
private boolean isSorted = false;
public void setIsFilterApplied(boolean isFilterApplied) {
this.isFilterApplied = isFilterApplied;
}
public void add(TableView<T> table){
ListChangeListener<T> change = c -> {
while (c.next()) {
if (c.wasAdded()) {
List<? extends T> added = c.getAddedSubList();
T lastAdded = added.get(0);
if (!isFilterApplied) {
newAdded.add(lastAdded);
}
}
}
};
table.getItems().addListener(change);
table.setRowFactory(param -> new TableRow<T>() {
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
return;
}
if (newAdded.contains(item)) {
if (isSorted) {
new Thread(()->{
Timeline flasher = new Timeline(
new KeyFrame(Duration.seconds(0.4), e -> pseudoClassStateChanged(flashHighlight, true)),
new KeyFrame(Duration.seconds(0.8), e -> pseudoClassStateChanged(flashHighlight, false))
);
flasher.setCycleCount(2);
flasher.play();
}).start();
if (item == newAdded.get(0)) {
newAdded.clear();
isSorted = false;
}
}else{
if(item == newAdded.get(0)){
isSorted = true;
}
}
}
}
});
}
}
Here ListChangeListener is associated with table.getItems() which helps me to record the newly inserted row.
It is possible to add multiple rows within one operation which means newAdded.size() can be larger than 1. What's more, rows are inserted from the top of the tableView(because I sort it with the Number.)
In tableView, not all rows are visible and updateItem methods only update those visible rows. My problem comes when these two situations happen(see below).
The first scenario
In first scenario, only 4 rows are visible, if user inserts 5 rows within one time, I cannot record the last row update(the updateItem won't be called for the new_row_5). Thereby, I cannot clear newAdded list (by doing newAdded.clear())
The second scenario
In the second scenario, only 4 rows are visible again. However, there are invisible rows both at top and bottom of the visible rows. If user inserts 2 rows, one will be visible and the other will be invisible. In my case, new_row_2 will flash while new_row_1 remains invisible. If user scrolls up the tableView when new_row_2 is flashing, he will see new_row_2 is flashing while new_row_1 is not which is really weird.
I also want to know if there is any way to find the number of visible rows.
I am still new to JavaFx and I don't know if this method is good or not. I hope someone can help me fix my problems. Thanks a lot!
Your approach doesn't seem like a clean way to do this. The animation depends on the TableRow the item is positioned in and does not seem to support multiple animations happening at the same time. Furthermore it relies on the equals method of the item class not being overridden and on the user not adding a item multiple times to the TableView. Also you potentially create a large number of Timelines (not necessary to start them from a seperate thread btw, since Timeline.play() does not block).
It's better to make the animation depend on the indices. Also keeping track of the TableRows created allows you to access existing cells, should they be be assigned a index that needs to be animated. Also you could handle the animations using a single AnimationTimer by storing the data in a suitable data structure.
Also it would IMHO be most convenient to use the rowFactory class to implement this logic.
The following example makes the rows flash whether they are on-screen or not.
public class FlashTableRowFactory<T> implements Callback<TableView<T>, TableRow<T>> {
private final static PseudoClass FLASH_HIGHLIGHT = PseudoClass.getPseudoClass("flash-highlight");
public FlashTableRowFactory(TableView<T> tableView) {
tableView.getItems().addListener((ListChangeListener.Change<? extends T> c) -> {
while (c.next()) {
if (c.wasPermutated()) {
int from = c.getFrom();
int to = c.getTo();
permutationUpdate(scheduledTasks, c, from, to);
permutationUpdate(unscheduledTasks, c, from, to);
}
if (c.wasReplaced()) {
addRange(c.getFrom(), c.getTo());
} else if (c.wasRemoved()) {
int from = c.getFrom();
int removed = c.getRemovedSize();
removeRange(scheduledTasks, from, from + removed);
removeRange(unscheduledTasks, from, from + removed);
modifyIndices(unscheduledTasks, from, -removed);
modifyIndices(scheduledTasks, from, -removed);
} else if (c.wasAdded()) {
int from = c.getFrom();
int to = c.getTo();
modifyIndices(unscheduledTasks, from, to - from);
modifyIndices(scheduledTasks, from, to - from);
addRange(from, to);
}
}
// remove all flashTasks that are older than the youngest for a
// given index
Set<Integer> indices = new HashSet<>();
removeDuplicates(unscheduledTasks, indices);
removeDuplicates(scheduledTasks, indices);
flashingIndices.clear();
updateFlash(lastUpdate);
refreshFlash();
if (!unscheduledTasks.isEmpty()) {
flasher.start();
}
});
this.tableView = tableView;
}
private static void removeDuplicates(List<FlashTask> list, Set<Integer> found) {
for (Iterator<FlashTask> iterator = list.iterator(); iterator.hasNext();) {
FlashTask next = iterator.next();
if (!found.add(next.index)) {
iterator.remove();
}
}
}
private static void modifyIndices(List<FlashTask> list, int minModify, int by) {
for (FlashTask task : list) {
if (task.index >= minModify) {
task.index += by;
}
}
}
private void addRange(int index, int to) {
for (; index < to; index++) {
unscheduledTasks.add(new FlashTask(index));
}
}
private static void removeRange(List<FlashTask> list, int from, int to) {
for (Iterator<FlashTask> iterator = list.iterator(); iterator.hasNext();) {
FlashTask next = iterator.next();
if (next.index >= from && next.index < to) {
iterator.remove();
}
}
}
private static void permutationUpdate(List<FlashTask> list, ListChangeListener.Change<?> c, int from, int to) {
for (FlashTask task : list) {
if (task.index < to && task.index >= from) {
task.index = c.getPermutation(task.index);
}
}
}
// set of item indices that should flash
private final Set<Integer> flashingIndices = new HashSet<>();
// references to every row created by this factory
private final List<SoftReference<TableRow<T>>> rows = new LinkedList<>();
// tasks waiting to be scheduled
private final List<FlashTask> unscheduledTasks = new LinkedList<>();
// tasks currently being animated
private final List<FlashTask> scheduledTasks = new LinkedList<>();
private static class FlashTask {
int index;
long schedulingTime;
public FlashTask(int index) {
this.index = index;
}
}
private final TableView<T> tableView;
private long lastUpdate;
/**
* Updates flashingIndices set
* #param now the current timestamp
* #return true if the set has been modified, otherwise false.
*/
private boolean updateFlash(long now) {
boolean modified = false;
for (Iterator<FlashTask> iterator = scheduledTasks.iterator(); iterator.hasNext();) {
FlashTask next = iterator.next();
// running time in seconds
double runningTime = (now - next.schedulingTime) / (1000d * 1000d * 1000d);
// slows down the animation for demonstration
final double animationSpeed = 0.1;
if (runningTime < 0.4 / animationSpeed) {
// no need to handle tasks that run for less than 0.4 seconds
break;
} else if (runningTime > 1.6 / animationSpeed) {
// end of task reached
iterator.remove();
modified |= flashingIndices.remove(next.index);
} else if (runningTime > 0.8 / animationSpeed && runningTime < 1.2 / animationSpeed) {
// second "inactive" interval during animation
modified |= flashingIndices.remove(next.index);
} else {
// activate otherwise
modified |= flashingIndices.add(next.index);
}
}
return modified;
}
private final AnimationTimer flasher = new AnimationTimer() {
#Override
public void handle(long now) {
lastUpdate = now;
// activate waiting flash tasks
for (FlashTask task : unscheduledTasks) {
task.schedulingTime = now;
}
scheduledTasks.addAll(unscheduledTasks);
unscheduledTasks.clear();
if (updateFlash(now)) {
refreshFlash();
if (scheduledTasks.isEmpty()) {
// stop, if there are no more tasks
stop();
}
}
}
};
/**
* Sets the pseudoclasses of rows based on flashingIndices set
*/
private void refreshFlash() {
for (Iterator<SoftReference<TableRow<T>>> iterator = rows.iterator(); iterator.hasNext();) {
SoftReference<TableRow<T>> next = iterator.next();
TableRow<T> row = next.get();
if (row == null) {
// remove references claimed by garbage collection
iterator.remove();
} else {
row.pseudoClassStateChanged(FLASH_HIGHLIGHT, flashingIndices.contains(row.getIndex()));
}
}
}
#Override
public TableRow<T> call(TableView<T> param) {
if (tableView != param) {
throw new IllegalArgumentException("This factory can only be used with the table passed to the constructor");
}
return new FlashRow();
}
private class FlashRow extends TableRow<T> {
{
rows.add(new SoftReference<>(this));
}
#Override
public void updateIndex(int i) {
super.updateIndex(i);
// update pseudoclass based on flashingIndices set
pseudoClassStateChanged(FLASH_HIGHLIGHT, flashingIndices.contains(i));
}
}
}

Looping through java recursive references

I was writing a simple linked list implementation and the corresponding Trigger class like this:
public class Trigger {
public static void main(String[] args) {
LinkedList lin = new LinkedList();
lin.add(3);
lin.add(4);
lin.add(5);
lin.display();
}
}
public class LinkedList {
Item startItem;
LinkedList() {
startItem = new Item();
}
public void add(Integer newValue) {
if(startItem.value == null) {
startItem.value = newValue;
return;
}
Item tempItem = new Item();
tempItem.value = newValue;
while(startItem.next != null) {
startItem = startItem.next;
}
startItem.next = tempItem;
}
public void display() {
while(true) {
System.out.println("\t"+ startItem.value);
if(startItem.next == null) {
break;
} else {
startItem = startItem.next;
}
}
}
class Item {
Integer value;
Item next;
Item() {
this.value = null;
this.next = null;
}
}
}
The problem is, only the last two additions are getting retained and the previous additions are discarded. This is (of course) because i keep changing the object that the reference startItem points to. My question is, given a recursive structure like this, what is the correct looping mechanism? I know that in a linked list, there is no need to get to the end of the list to perform addition. Linked list structure is used as a context to ask about looping through recursive structures. Thanks.
Your basic structure is correct. You just need to add one line before you start looping...
Item currentItem = startItem;
while(currentItem.next != null) {
currentItem = currentItem.next;
}
The only time the field startItem should be changed is in your first check if its value is null. It's reference should never be changed as it should always point to the start of the structure. Consider making it final to enforce this.

ArrayList of custom Java objects over BlazeDS into AS3.0

Right away i just try to explain my problem:
Using BlazeDS i got the following Javaclasses:
DatabaseService.java:
public class Database {
private Category helpCat = null;
private Category root = new Category(1, "root", null, null);
private List<Article> database;
public Database()
{
// ------------ tree -----------------------------------------------------------------------
List<Category> level_one = new ArrayList<Category>();
List<Category> level_two_computer = new ArrayList<Category>();
List<Category> level_tree_hardware = new ArrayList<Category>();
// Level 1
Category buecher = new Category(2, "buecher", root, null);
Category computer = new Category(3, "computer", root, level_two_computer);
level_one.add(buecher);
level_one.add(computer);
//Level 2
Category hardware = new Category(4, "hardware", computer, level_tree_hardware);
Category software = new Category(5, "software", computer, null);
level_two_computer.add(hardware);
level_two_computer.add(software);
//Level 3
Category graphic = new Category(6, "graphic", hardware, null);
Category sound = new Category(7, "sound", hardware, null);
level_tree_hardware.add(graphic);
level_tree_hardware.add(sound);
// Level 0
root.addChilds(level_one);
// ------ tree end ----------------------------------------------------------------------------
database = new ArrayList<Article>();
try {
add(new Book("Per Anhalter durch die Galaxis", "42", Articletype.BOOK, 795, "Per Anhalter durch die Galaxiss", "Douglas Adams", "Heyne Verlag", "Taschenbuch", "3453146972"), buecher);
add(new Book("Harry Potter und der Stein der Weisen", "descriptionShort", Articletype.BOOK, 1299, "Harry Potter und der Stein der Weisen", "Joanne K. Rowling", "Carlsen Verlag GmbH", "gebunden", "3551551677"), buecher);
add(new Book("Harry Potter und die Kammer des Schreckens", "descriptionShort", Articletype.BOOK, 1499, "Harry Potter und die Kammer des Schreckens", "Joanne K. Rowling", "Carlsen Verlag GmbH", "gebunden", "3551551677"), buecher);
add(new Hardware("nVidia GeForce 8400GS", "Graphikkarte", Articletype.HARDWARE, 2665, "512 GDDR5 Speicher, DVI, 1 GPU", "MSI", "neu"), graphic);
add(new AKW("Biblis C", "Druckwasserreaktor, Preis auf Anfrage, Nur Selbstabholer", Articletype.AKW, -1, "Biblis", 0, 2525, "Siemens", 1, 2012), software);
} catch (Exception e) {
e.printStackTrace();
}
}
public List<Category> getCategories(String node) {
if(node.equalsIgnoreCase("root"))
return root.getChildren();
Category baum = null;
baum = get_node_by_name(root, node);
return baum.getChildren();
}
private Category get_node_by_name(Category localroot, String lookfor)
{
helpCat = null;
if(localroot.getChildren() != null)
{
for (int i = 0; i < localroot.getChildren().size(); ++i)
{
if(!(localroot.getChild(i).getName().equals(lookfor)))
{
get_node_by_name(localroot.getChild(i), lookfor);
}
else
{
helpCat = localroot.getChild(i);
helpCat.setParent(null);
}
}
}
return helpCat;
}
public List<Article> search(int artID, String name, Category categorie){
List<Article> ergebnis = new ArrayList<Article>();
if (artID >= 0)
{
for(int i = 0; i< database.size(); ++i){
if(database.get(i).getID() == artID)
{
ergebnis.add(database.get(i));
return ergebnis;
}
}
}
if (name != null){
for(int i = 0; i<database.size(); ++i){
if (database.get(i).getName().equalsIgnoreCase(name))
ergebnis.add(database.get(i));
}
return ergebnis;
}
if (categorie != null){
{
ergebnis.addAll(categorie.getArticles());
}
return ergebnis;
}
return database;
}
public Article add(Article newArticle, Category cat) throws Exception
{
newArticle.addCategory(cat);
if(newArticle.getID() != 0)
{
throw new Exception("Die Artikel ID wird vom DBS festgelegt!");
}
if (database.isEmpty())
{
newArticle.setID(0);
}
else
{
newArticle.setID(database.get(database.size() - 1).getID()+1);
}
database.add(newArticle);
return newArticle;
}
}
And the Category Class:
public class Category {
private int idCat;
private String nameTEST;
private Category parent = null;
private List<Article> articles = new ArrayList<Article>();
private List<Category> children = new ArrayList<Category>();
public Category(int _id, String _name, Category _parent, List<Category> _children)
{
if(_id > 0)
idCat = _id;
if(_name != null)
nameTEST = _name;
if(_parent != null)
parent = _parent;
if(_children != null)
children = _children;
}
public String toString()
{
return nameTEST;
}
void addArticle(Article article){
articles.add(article);
}
public List<Article> getAllArticles(){
List<Article> ergebnis = this.getArticles();
for (int i = 0; i<children.size();++i){
ergebnis.addAll(children.get(i).getAllArticles());
}
return ergebnis;
}
public void setID(int iD) {
idCat = iD;
}
public int getID() {
return idCat;
}
public void setName(String name) {
this.nameTEST = name;
}
public String getName() {
return nameTEST;
}
/**
* #param parent the parent to set
*/
public void setParent(Category parent)
{
this.parent = parent;
}
/**
* #return the articles
*/
public List<Article> getArticles()
{
return articles;
}
public void addChilds(List<Category> _next)
{
for (int i = 0; i < _next.size(); ++i)
{
children.add(_next.get(i));
}
}
public void addChild(Category one_next)
{
children.add(one_next);
}
public Category getChild(int index)
{
return children.get(index);
}
public void removeChild(Article article){
articles.remove(article);
}
public List<Category> getChildren()
{
return this.children;
}
}
also there are of course classes for articles and so on, but thats not important at that point.
the counterpart in flex looks like this:
Category.as
[RemoteClass(alias="PACKAGE.Category")]
public class Category
{
private var idCat:int = -1;
private var nameTEST:String = null;
private var parent:Category = null;
private var articles:ArrayCollection = new ArrayCollection;
private var children:ArrayCollection = new ArrayCollection;
public function Category(id:int, name:String, parent:Category, childlist:ArrayCollection, articles:ArrayCollection = null)
{
this.idCat = id;
this.nameTEST = name;
this.parent = parent;
this.articles = articles;
this.children = childlist;
}
public function setChildren(childList:ArrayCollection):void
{
this.children = childList;
}
public function getChildren():ArrayCollection
{
return this.children;
}
public function getName():String
{
return this.nameTEST;
}
}
Then i got a Flex service class calling BlazeDS and executing the getCategories java method. Since Flash dosn't seem to understand typed arrays, the result from that method which i get back in flex is a simple array of untyped objects (the mapping dosn't seem to work here, even tought the class category exists in flex and has the same properties).
thats the first thing. but however, i'm converting the untyped objects manually into objects of the category.as class.
the second thing is that categories have child-categories within the java object, which are also ArrayLists of the type category.java. the problem about that: my result event object only contains the first level of categories, looking into them the children are allways null. i dunno why they are empty, since they ARE part of the java object category.
and the third thing (the strangest by fast), you maybe noticed i named the properties of the category.java class strange, like idCat and nameTest instead of simply id and name. why that? because the property names of my flex result objects dont seem to change when i change the java objects properties names (result object properties are named "id" and "name" but the java class object properties are named "idCAT" and "nameTEST"). that it REALLY strange, since if i set the properties, like you see at nameTEST = "TESTNAME" it IS recogniced by flex, only the proertyNAMES dont seem to be recognized at all.
is blaze DS saving / caching the mapping configuration somewhere? how do i get it to rebuild the hole mappings IF so?
that could also explain my problem about the untyped objects i get from java, since before i changed the lists into ArrayLists they where vectors ( which blazeDS dosn't support AFAIK), and maybe not only the propertynames, but also the propertytypes are hard-mapped at some wired place and blazeds just dosn't get them refreshed.
i really like checked everything 5 times by now, even redeployed blazeds on the server to make sure no mappings left, but it didnt help at all.
ANY ideas what i could do? (exept changing to another serializer then blazeds (thats what i'm going to do if everything else fails...))
i have the same issues, but if you can warm up the tree before call the method, it will be ok.
what i mean "warm up" is you iterator the arraylist without doing anything. it is flunky why this work!
I have had similar problems with a list returned from a service not including the child elements in the list. I have found that BlazeDS can return a typed list. The two things that fixed this for me were:
a) Ensure that the returned list is of type java.util.List (not java.util.ArrayList for example).
b) Ensure that the class for the elements in the list have both public setters and getters for all entities to be returned.
For example - the following code works for me:
public class OrganisationService {
...
public List<Organisation> getOrganisations() {
List<Organisation> list = new ArrayList<Organisation>();
...
return list;
}
...
}
As mentioned elsewhere, you need to initiailize your AS3 remote objects so that it is included in the SWF during compilation.
Somewhere in your code, add:
var cat:Category = new Category();
var art:Article = new Article();
That should fix your generic object issue. (I add mine all in one spot, in an application start up method).
It turns out that a simple missmatch of the classnames was the cause of all evil. still some problems to solve, but atleast i get sub-arrays returned now, only the objects are still simple AS 3 objects and not the specified "Category" and "Article" objects, but i think thats because i dont have all methods included and mapping is failing because of that.
but thanks for your offer, i appreciate that.

Categories

Resources