ClassCastException in JavaFX - java

My code gets the following error.
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: javafx.scene.Group cannot be cast to javafx.scene.control.TreeCell
Source code
private TreeItem getClickedTreeItem(EventTarget eventTarget){
TreeItem clickedTreeItem = null;
if(eventTarget instanceof TreeCellSkin){
clickedTreeItem = (TreeItem) ((TreeCell) ((TreeCellSkin)eventTarget).getParent()).getTreeItem();
}else if(eventTarget instanceof LabeledText){
clickedTreeItem = (TreeItem) ((TreeCell) ((LabeledText)eventTarget).getParent().getParent()).getTreeItem();
}else if(eventTarget instanceof ImageView){
clickedTreeItem = (TreeItem) ((TreeCell) ((ImageView)eventTarget).getParent().getParent()).getTreeItem();
}
return clickedTreeItem;
}
The console says this line:
clickedTreeItem = (TreeItem) ((TreeCell) ((ImageView)eventTarget).getParent().getParent()).getTreeItem();
This is legacy code that worked with Java 6, but gives the above exception using Java8?
What could be causing the ClassCastException now, and how to fix it for Java8?

The current code is brittle as it relies on the internal structure of the TreeCell. Also, TreeCellSkin1 and LabeledText are both internal classes. Internal code is subject to change without notice and without regards to third party reliance on it. Since this worked in Java 6 but not Java 8 I can only assume that the ImageView's grandparent changed from being the TreeCell to being a Group between the two versions.
To fix this you could look into the implementation and see what you need to do so you reach the TreeCell again, but that wouldn't really solve the problem. The use of EventTarget says to me this code was implemented while not fully understanding how event handling works in JavaFX. From the apparent goal of this code you should be using the source of the event, not the target. In JavaFX, the source of the event is always the object for which the EventHandler currently handling said Event was added to2. In other words, if you added the EventHandler to the TreeCell then the source will be the TreeCell. Using the source, and assuming EventHandler is added to the TreeCell, you can simply do:
TreeItem<?> item = ((TreeCell<?>) event.getSource()).getTreeItem();
Of course, if you're adding the EventHandler to the TreeCell you likely needn't bother with the source as you'll have access to the TreeCell directly. For example:
TreeView<String> treeView = new TreeView<>();
treeView.setCellFactory(tv -> {
TreeCell<String> cell = new TreeCell<>(); // or some custom implementation
cell.setOnMouseClicked(event -> {
TreeItem<String> item = cell.getTreeItem();
// do something with item...
});
return cell;
});
1. TreeCellSkin became public API in JavaFX 9 along with many (all?) skin implementations. They are part of the javafx.scene.control.skin package.
2. There's more to it but that's beyond the scope of this answer.

Related

Binding for userdata change of nodes inside a list?

I'm building a JavaFX application where I have a GridPane of 10 labels, and the user can assign objects to these labels per drag and drop. I used the userdata property of nodes, so when the user drops an object onto one label, it sets that object as userdata of this label.
Now I want to set a bind for the disableProperty of a button, so that the button only gets enabled when the user "filled" all of these labels with data. I've tried several things, finally I came to something like this:
FilteredList<Node> emptySlots = gridPane.getChildren().filtered(node -> node.getUserData() == null);
SimpleListProperty<Node> listProperty = new SimpleListProperty<>(emptySlots);
BooleanProperty hasEmptySlots = new SimpleBooleanProperty();
hasEmptySlots.bind(not(listProperty.emptyProperty()));
button.disableProperty().bind(hasEmptySlots);
But sadly the emptySlots list doesn't seem to update when the userdata property of the labels gets changed. I already tried to find ways to update this list, but I only find ways that involve ObservableLists with own classes and custom setup when the list is created like this (from here):
ObservableList<Model> masterData = FXCollections.observableArrayList<>(model ->
new Observable[]{model.statusProperty()});
But I don't have that option here.
Has anyone an idea how to archieve this?
Using an extractor could have worked using a mirror of the children list, but unfortunately the userData is not stored in a property (i.e. it's not Observable). To do what you want you'll have to use a different mechanism to store your user's objects.
An alternative to using the userData, in the same spirit of your current approach, is to use a Node's properties. These properties are stored in an ObservableMap<Object, Object> which means they can be observed for changes. To use this option you'll still want to use an extractor and mirror the children list.
class Foo {
private static final String USER_OBJECT_KEY = "USER_OBJECT";
private final GridPane gridPane = ...;
private final Button button = ...;
Foo() {
ObservableList<Node> mirror = FXCollections.observableArrayList(
n -> new Observable[]{n.getProperties()}
);
Bindings.bindContent(mirror, gridPane.getChildren());
FilteredList<Node> filtered = mirror.filtered(
n -> !n.getProperties().containsKey(USER_OBJECT_KEY)
);
button.disableProperty(Bindings.isEmpty(filtered));
}
}
The above uses Bindings.isEmpty(ObservableList) rather than wrapping the observable list in a ListProperty.
You may also want to consider moving this state out into a model. That way you avoid coupling the state of the application with JavaFX GUI objects (the model should know nothing about the view).

JMeter as code assertions are not being considered in test results

I'm using JMeter as a code (programmatic approach instead of GUI, with a Java Maven project) in order to stress-test an AWS Lambda Serverless API.
I've already developed a test plan, thread group, HTTPSamplerProxy and so on...
The execution of the calls to the API works perfect but is not the case e.g. for the DurationAssertion I've added to the HTTP Sampler..
I've also set a CSV file for the output where after execution I see everything ok (status code 200..), but the test should fail due to it is over the DurationAssertion I've configured (in addition to other assertion test elements).
I thought that perhaps I had to set "enabled" = true in the DurationAssertion object, but no effect.. Also, I've tried to access the JMeter Context in this way:
JMeterContextService.getContext().getPreviousResult()
I expected above code to retrieve a SampleResult (which has an AssertionResult collection), but the SampleResult is null..
A test plan with test elements (DurationAssertion in this case) without its respective analysis of the results of these assertions make no sense.. I want to see a failure message in each call that exceeds a certain threshold.. If I'd be using the JMeter GUI, I would add a ViewResultTree, which shows a Sampler Result view with detail of the request, response, and associated test asserts. And in addition to assertion result (per each request) I wanna see the request payload, full response, headers.. But in programmatic mode (without using the GUI).
So I would highly appreciate if anyone could give me some hint in how to accomplish this goal but by code.
UPDATE 1: I share a github snippet with the entire source code, such as UBIK LOAD PACK user suggested me:
https://gist.github.com/svillarreal/5eb90a66b8972633b95c249abb3566da
UPDATE 2: Inspection of context object (evaluated after JMeter engine finished run) - All null inside
UPDATE 3
i) I've recently found a jmeter.properties file, where I've configured the following properties:
jmeter.save.saveservice.output_format=xml
jmeter.save.saveservice.assertion_results=all
And now the output as XML instead of CSV shows, at least, the sent request payload and the response data, which is VERY useful for analyse error cases.
ii) I did the inspection of JMeterContextService.getContext() inside the JMeterEngine execution instead of after it finishes run and then I could realize that there is one context per thread group, and during its run this object is full so now is clear why in UPDATE 2 all the properties are null..
Best regards and thanks!
I can think about at least one use case when your approach will not work: JMeter didn't receive response from the server at all.
For example if your server gets overloaded it might be the case that JMeter will never get response back therefore your Duration Assertion will simply not be applied as PostProcessors, Listeners and Assertions are not fired given that SampleResult is null.
So in order to be on the safe side I would recommend applying connect and response timeouts to your HTTP Request sampler(s)
HTTPSamplerProxy httpSampler = new HTTPSamplerProxy();
httpSampler.setConnectTimeout("3000");
httpSampler.setResponseTimeout("3000");
//etc.
If you have > 1 HTTP Request sampler in Test Plan it makes sense to go for HTTP Request Defaults instead of setting the timeouts individually.
Finally I could fix this. The issue was that I was managing erroneously the tree that is passed to the StandardJMeterEngine.
In JMeter everything is based on this tree, and like in the GUI, we should take care about how the elements are positioned in its hierarchy.
Analysing the library and debugging it intensely I've realized more in deep how JMeter works and I've understood that everything is managed starting from the HashTree. So the solution was to add the DurationAssertion and ResponseAssertion as HTTPSamplerProxy node's childs instead of putting them as HTTPSamplerProxy's test elements.
In particular, the method that fills the assertions to check after the execution is the following (and that let me know how to manage the hashtree):
// org.apache.jmeter.threads.TestCompiler
private void saveSamplerConfigs(Sampler sam) {
List<ConfigTestElement> configs = new LinkedList<>();
List<Controller> controllers = new LinkedList<>();
List<SampleListener> listeners = new LinkedList<>();
List<Timer> timers = new LinkedList<>();
List<Assertion> assertions = new LinkedList<>();
LinkedList<PostProcessor> posts = new LinkedList<>();
LinkedList<PreProcessor> pres = new LinkedList<>();
for (int i = stack.size(); i > 0; i--) {
addDirectParentControllers(controllers, stack.get(i - 1));
List<PreProcessor> tempPre = new LinkedList<>();
List<PostProcessor> tempPost = new LinkedList<>();
List<Assertion> tempAssertions = new LinkedList<>();
for (Object item : testTree.list(stack.subList(0, i))) {
if (item instanceof ConfigTestElement) {
configs.add((ConfigTestElement) item);
}
if (item instanceof SampleListener) {
listeners.add((SampleListener) item);
}
if (item instanceof Timer) {
timers.add((Timer) item);
}
if (item instanceof Assertion) {
tempAssertions.add((Assertion) item);
}
if (item instanceof PostProcessor) {
tempPost.add((PostProcessor) item);
}
if (item instanceof PreProcessor) {
tempPre.add((PreProcessor) item);
}
}
assertions.addAll(0, tempAssertions);
pres.addAll(0, tempPre);
posts.addAll(0, tempPost);
}
SamplePackage pack = new SamplePackage(configs, listeners, timers, assertions,
posts, pres, controllers);
pack.setSampler(sam);
pack.setRunningVersion(true);
samplerConfigMap.put(sam, pack);
}
Also I had to activate the following property:
jmeter.save.saveservice.assertion_results_failure_message=true
As a consequence now I have my CSV file report with the assertions results messages included in a exclusive column for that.
Well, issue resolved. ** I've updated the github snippet gist with the final solution ** Many thanks to all that read this post and tried to collaborate.
Best regards,

ASTRewrite for a QuickFix: How to position the cursor?

I work on a Eclipse Plugin and create a QuickFix using ASTRewrite. Briefly it's constructed in the following way:
public class MyQFXProcessor implements IQuickFixProcessor {
public IJavaCompletionProposal[] getCorrections(IInvocationContext context,
IProblemLocation[] locations) {
AST ast = context.getASTRoot().getAST();
ASTRewrite rw = ASTRewrite.create(ast);
ASTNode replacement = ast.newSimpleName("Test");
rewrite.replace(context.getCoveringNode(), replacement);
IJavaCompletionProposal p = new ASTRewriteCorrectionProposal("My QFX",
context.getCompilationUnit(), rw, 10);
return new IJavaCompletionProposal[]{p};
}
}
This works fine so far. But what I didn't manage to achieve yet is to set the desired cursor position after the proposal is applied. For example this is how it's solved in JDT for add argument quick fix:
I think there should be an API for doing that, because Eclipse uses this kind of behavior for different use-cases (in auto-completion among others). Does anybody have an idea how to implement that?
You can check the internal implementation and usage of org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedCorrectionProposal which sets the end position of the linked mode in a quick fix or quick assist via org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedCorrectionProposal.setEndPosition(ITrackedNodePosition position).

How to set the input for the UI part in eclipse e4?

I am using eclipse e4 application. I am using the eventBroker to pass values from one part to another part. If many parts(Kind of tabs) are open , how to pass values to the part(tab) that is currently selected. ? I am using the #UIEventTopic to get the values for the part. But the problem is ,the values are replicated to all the tabs. In other words , I am trying to show different JFreechart in different tabs, but the charts are replicated to the previous tabs.
Can anyone please suggest me some ideas?
Thanks in advance
The event broker always broadcasts to anything that is dealing with the event, you can't use it to send to one specific thing.
If you are in a Handler you can get the current part in the #Execute method and set a value directly in your class - something like:
#Execute
public void execute(#Named(IServiceConstants.ACTIVE_PART) MPart activePart)
{
Object part = activePart.getObject();
if (part instanceof MyClass)
{
((MyClass)part).setValue(xxxx);
}
}
Update:
If you are in another part use the EPartService to get the active part:
#Inject
EPartService partService;
...
MPart activePart = partService.getActivePart();
Object part = activePart.getObject();
if (part instanceof MyClass)
{
((MyClass)part).setValue(xxxx);
}
You can also use EPartService.findPart("part id") to find a part with a given id.

Type of iterator erroneous

I am working on a project in java and I am developing a API for custom components. I am using an interface called DrawHandler to allow the components to draw to a graphics object. To do this every time a component is added I need to add it's DrawHandlers to a list. I am using this code
public void addComponent(Component c){
//Add to list of components
components.add(c);
//Add all of the components drawHandlers so the component can be drawn
List<DrawHandler> dhs = c.getDrawHandlers();
Iterator<DrawHandler> i = dhs.iterator();
while(i.hasNext()){
addDrawHandler(i.next());
}
}
However when I get an error on this line
Iterator<DrawHandler> i = dhs.iterator();
and this line
addDrawHandler(i.next())
The error is:
The type of next() is erroneous : where E is a type-variable:
E extends Object declared in interface Iterator
If I use this code as sugested
public void addComponent(Component c){
//Add to list of components
components.add(c);
//Add all of the components drawHandlers so the component can be drawn
for(DrawHandler handler : c.getDrawHandlers())
addDrawHandler(handler);
}
I get this error when i compile it :
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - Erroneous tree type: windows.DrawHandler
at windows.MainWin.addComponent(MainWin.java:37)
at windows.Main.main(MazeNavigator.java:21)
Any sugjestions? What am I doing wrong? thanks for your your help
It doesn't explain your error but you may find it compiles fine if you write
for(DrawHandler handler : c.getDrawHandlers())
addDrawHandler(handler);
Uncompilable source code - Erroneous tree type: windows.DrawHandler
This means you are using an option which allows you to run code which doesn't compile. I suggest you turn this option off and you should see the true cause of your error.

Categories

Resources