Get all registered listeners to an ObservableValue - javafx

How to get all listeners to an observable value? I could extend the class and override addListener and removeListener methods to store them in a set. But the set should already be stored somehow inside observable value. How could I get that set?

I find a way around it, you can't get direct access to the Listeners list but if you use a debugger(I use IntelliJ) you can see it if you look inside your ObservableProprty like this:(I hope this is clear enough)
another way:(You're a smart guy, you'll know how to fit into your case)
//SimpleFloatProperty we want to find its Listeners
FloatPropertyBase f=ampPS.currentProperty();
Object value;
ChangeListener[] list;
ChangeListener changeListener=null;
Field field = null;
try {
field = FloatPropertyBase.class.getDeclaredField("helper");
field.setAccessible(true);
value = field.get(f);
try {
field = value.getClass().getDeclaredField("listener");
field.setAccessible(true);
changeListener =(WeakChangeListener)field.get(value);
}catch (NoSuchFieldException e) {
e.printStackTrace();
}
try {
field = value.getClass().getDeclaredField("changeListeners");
field.setAccessible(true);
list =(ChangeListener[])field.get(value);
}catch (NoSuchFieldException e) {
e.printStackTrace();
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
the result:
(Notice the difference between 1 listener or more than 1)
I I'm adding Example for several types of values
public static ChangeListener[] getChangeListeners(ObservableValue observableValue){
Object value;
ChangeListener[] list=null;
ChangeListener changeListener=null;
Field field = null;
try {
if(observableValue instanceof SimpleFloatProperty ){
field = FloatPropertyBase.class.getDeclaredField("helper");
}
else if(observableValue instanceof SimpleBooleanProperty ){
field = BooleanPropertyBase.class.getDeclaredField("helper");
}
else if(observableValue instanceof SimpleIntegerProperty ){
field = IntegerPropertyBase.class.getDeclaredField("helper");
}
field.setAccessible(true);
value = field.get(observableValue);
try {
field = value.getClass().getDeclaredField("listener");
field.setAccessible(true);
changeListener =(ChangeListener)field.get(value);
}catch (NoSuchFieldException e) {
//e.printStackTrace();
}
try {
field = value.getClass().getDeclaredField("changeListeners");
field.setAccessible(true);
list =(ChangeListener[])field.get(value);
}catch (NoSuchFieldException e) {
//e.printStackTrace();
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if(list!=null){
return list;
}
else {
ChangeListener[] changeListeners = new ChangeListener[1];
changeListeners[0]=changeListener;
return changeListeners;
}
}
and I call it:
SimpleBooleanProperty booleanProperty = new SimpleBooleanProperty(true);
SimpleFloatProperty simpleFloatProperty = new SimpleFloatProperty(0);
SimpleIntegerProperty simpleIntegerProperty = new SimpleIntegerProperty(1);
booleanProperty.addListener(changeListener);
simpleFloatProperty.addListener(changeListener);
simpleIntegerProperty.addListener(changeListener);
simpleIntegerProperty.addListener(changeListener);
System.out.println(getChangeListeners(booleanProperty).length);
System.out.println(getChangeListeners(simpleFloatProperty).length);
System.out.println(getChangeListeners(simpleIntegerProperty).length);
the result:
so I do get warnings, but the job is done!

The documentation for ObservableValue.removeListener states:
If the given listener has not been previously registered (i.e. it was never added) then this method call is a no-op.
This leaves a few options if reflection is to be avoided.
First, call removeListener before adding the listener, such as:
final var property = someProperty();
final var listener = getListener();
property.removeListener( listener );
property.addListener( listener );
This technique is equivalent to using a Set, provided getListener() always returns the same object reference. (This may also work if different object references of the same class override equals to return true, but you'd have to double-check.)
The downside is having to keep a reference to the listener that was added, which could require a new class, but will at least require a new instance variable.
Second, keep a map of registered listeners, something to the effect of:
final HashMap<ObservableValue<?>, Object> map = new HashMap<>();
final var property = someProperty();
final var listener = getListener();
map.computeIfAbsent( property, p -> {
property.addListener( listener );
return property;
});
Although the question is looking for the list of listeners, I suspect the intent of the question is to avoid adding duplicate listeners, which is a common scenario.

Related

Access fields of a TestNG test class from a TestListenerAdapter

Background
I have the following situation:
My test-classes implement org.testng.ITest
They all have a Helper containing info on the current test environment (e.g. device-under-test)
For example:
com.company.appundertest.Helper h;
public class TestClass implements org.testng.ITest {
private String testName;
//Helper is initialized externally in Factory + DataProvider
//and passed to Constructor.
public TestClass(com.company.appundertest.Helper hh) {
this.h = hh;
//constructor sets the test-name dynamically
//to distinguish multiple parallel test runs.
this.testName = "some dynamic test name";
}
#Override
public String getTestName() {
return this.testName;
}
#Test
public void failingTest() {
//test that fails...
}
}
These test-classes are executed in parallel using Factory and parallel data-provider.
Upon Test Failure, I need to access variables within the Helper instance of the failing test-class. These will be used to identify the environment at the point of failure (e.g. take screenshot on failing device).
This problem essentially boils down to:
How would I access fields within the TestNG test-class?
References
Access to private inherited fields via reflection in Java
Here's an example method. You can insert this in a Test Listener class (which extends TestListenerAdapter)
public class CustomTestNGListener extends TestListenerAdapter{
//accepts test class as parameter.
//use ITestResult#getInstance()
private void getCurrentTestHelper(Object testClass) {
Class<?> c = testClass.getClass();
try {
//get the field "h" declared in the test-class.
//getDeclaredField() works for protected members.
Field hField = c.getDeclaredField("h");
//get the name and class of the field h.
//(this is just for fun)
String name = hField.getName();
Object thisHelperInstance = hField.get(testClass);
System.out.print(name + ":" + thisHelperInstance.toString() + "\n");
//get fields inside this Helper as follows:
Field innerField = thisHelperInstance.getClass().getDeclaredField("someInnerField");
//get the value of the field corresponding to the above Helper instance.
System.out.println(innerField.get(thisHelperInstance).toString());
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Call this as follows:
#Override
public void onTestFailure(ITestResult tr) {
getCurrentTestHelper(tr.getInstance());
}
The #Vish 's solution is good, but you can avoid reflection with:
interface TestWithHelper {
Helper getHelper();
}
where your TestClass will implement it.
Then:
private void getCurrentTestHelper(Object testClass) {
if (testClass instanceof TestWithHelper) {
Helper helper = ((TestWithHelper) testClass).getHelper();
...
}
}

Error Handling in Web Form

I'm trying to cater for an error in my data access layer, which would return an int of value -1. See below:
protected void FolderBtn_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
try
{
DocsDALC dalc = new DocsDALC();
// Updating two tables here - Folders and FolderAccess tables
// - as an ADO.NET transaction
int folderID = dalc.CreateFolder(...);
if (folderID > 0)
{
Response.Redirect(Request.Url.ToString(), false);
// Re-construct this to include newly-created folderID
}
else
{
// How do I throw error from here?
}
}
catch (Exception ex)
{
HandleErrors(ex);
}
}
}
If data layer returns -1, how can I throw an error from within the try block?
As simply as the following - however, since you are catching errors, if you know it's a problem, it would be better to call an overload of HandleErrors method that you could pass in a string defining the problem, rather than throw the exception (which is costly for what this will do).
If you still want to throw the exception:
if (folderID > 0)
{
Response.Redirect(Request.Url.ToString(), false);
// Re-construct this to include newly-created folderID
}
else
{
throw new Exception("Database returned -1 from CreateFolder method");
}
A possible alternative:
if (folderID > 0)
{
Response.Redirect(Request.Url.ToString(), false);
// Re-construct this to include newly-created folderID
}
else
{
HandleErrors("Database returned -1 from CreateFolder method");
}
With of course an overloaded HandleErrors method.

Fragment Replace null pointer exception

I am trying to replace fragment with another inside a onItemClick of list view and i want to send a selected item name from list fragment to another fragment but its showing null pointer exception
#Override
public void onItemClick(AdapterView arg0, View view, int position,
long id)
{
try
{
Log.e("----- inside onItemClick -----", citySelected);
MapVisibleFragment newFragment = new MapVisibleFragment();
Bundle args = new Bundle();
args.putString("CITYNAME", citySelected);
newFragment.setArguments(args);
((BaseContainerFragment) getParentFragment()).replaceFragment(
newFragment, true);
} catch (Exception e)
{
e.printStackTrace();
}
}
but the same concept is working inside button click for the below one
try
{
ListFragmentFromDBRecords2 newFragment = new ListFragmentFromDBRecords2();
Bundle args = new Bundle();
args.putStringArrayList("names", allData);
newFragment.setArguments(args);
((BaseContainerFragment) getParentFragment()).replaceFragment(
newFragment, true);
Log.d("--->>>>", allData.toString());
}
catch (Exception e)
{
e.printStackTrace();
}
Based on your comment
((BaseContainerFragment) getParentFragment())
is null for you. Check why getParentFragment() method returns null.

interesting service behaviour in silverlight

I have a Silverlight project which takes some encrypted string thru its Service Reference: DataService (service which is done in an ASP.NET project).
The method from TransactionServices.cs to get the encrypted string is:
public void GetEncryptedString(string original)
{
DataService.DataServiceClient dataSvc = WebServiceHelper.Create();
dataSvc.GetEncryptedStringCompleted += new EventHandler<SpendAnalyzer.DataService.GetEncryptedStringCompletedEventArgs>(dataSvc_GetEncryptedStringCompleted);
dataSvc.GetEncryptedStringAsync(original);
}
On completing, put the result in encodedString var (which is initialized with an empty value):
void dataSvc_GetEncryptedStringCompleted(object sender, SpendAnalyzer.DataService.GetEncryptedStringCompletedEventArgs e)
{
if (e.Error == null)
{
try
{
if (e.Result == null) return;
this.encodedString = e.Result;
}
catch (Exception ex)
{
Logger.Error("TransactionService.cs: dataSvc_GetEncryptedStringCompleted: {0} - {1}",
ex.Message, ex.StackTrace);
MessageBox.Show(ex.ToString());
}
}
}
Now I want to get the encoded string from my MainPage.xaml like:
TransactionService ts = new TransactionService();
ts.GetEncryptedString(url);
Console.WriteLine(ts.encodedString);
I do not uderstand why ts.encodedString is empty. When I do the debug I see that it actually prints out empty and AFTER that it goes to the void dataSvc_GetEncryptedStringCompleted to take the result and fill it.
Can you point me what I've done wrong? Is there a way to wait for the encodedString to be fetched and only after that to continue?
Thanks a lot.
When you call the ts.GetEncryptedString(url); you just started async operation. And therefor the value you are accessing is will be set only in the callback method.
But you access it before the value is modified by the callback.
The solution which I am using will looks similar to folowing:
Redefine the GetEncryptedString method signature.
public void GetEncryptedString(string original, Action callback)
{
DataService.DataServiceClient dataSvc = WebServiceHelper.Create();
dataSvc.GetEncryptedStringCompleted += (o,e) =>
{
dataSvc_GetEncryptedStringCompleted(o,e);
callback();
}
dataSvc.GetEncryptedStringAsync(original);
}
Call it like this:
ts.GetEncryptedString(url, OtherLogicDependantOnResult);
where
OtherLogicDependantOnResult is
void OtherLogicDependantOnResult()
{
//... Code
}

Unable to hook into PropertyChanged event using MVVM-Light

Greetings, creating my first MVVM based WPF app and trying to figure out why I'm unable to hook into the PropertyChanged event of a dependency property.
Code in the parent view model:
void createClients()
{
var clients = from client in Repository.GetClients()
select new ClientViewModel(Repository, client);
foreach (var client in clients)
{
client.PropertyChanged += onClientPropertyChanged;
}
Clients = new ViewableCollection<ClientViewModel>(clients);
Clients.CollectionChanged += onClientsCollectionChanged;
}
// Never gets called
void onClientPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Name")
{
//...
}
}
ViewableCollection is a simple extension of ObservableCollection to encapsulate a View.
In the ClientViewModel the setters are being called but RaisePropertyChanged isn't working as I would expect, because onClientPropertyChanged isn't being invoked. Both view models inherit from ViewModelBase.
public string Name
{
get { return client.Name; }
set
{
if (value == client.Name) return;
client.Name = value;
RaisePropertyChanged("Name");
}
}
If I wire up PropertyChanged to a method inside the ClientViewModel then it is being fired, so I'm stumped as to why this isn't working in the parent view model. Where am I going wrong?
This SO question explains the problem; ObservableCollection protects the PropertyChanged event.
One solution is to use MVVM-Light Messenger:
void createClients()
{
var clients = from client in Repository.GetClients()
select new ClientViewModel(Repository, client);
Clients = new ViewableCollection<ClientViewModel>(clients);
Clients.CollectionChanged += onClientsCollectionChanged;
Messenger.Default.Register<PropertyChangedMessage<string>>(this, (pcm) =>
{
var clientVM = pcm.Sender as ClientViewModel;
if (clientVM != null && pcm.PropertyName == "Name")
{
// ...
}
});
}
createClients() should be refactored, but for consistency with the question code I'll leave it in there. Then a slight change to the property setter:
public string Name
{
get { return client.Name; }
set
{
if (value == client.Name) return;
string oldValue = client.Name;
client.Name = value;
RaisePropertyChanged<string>("Name", oldValue, value, true);
}
}

Resources