NullPointerException in binding - javafx

Hi I would like to bind a value depending other object. It this object is null the value will be set by default. But I still received NullPointerException. I expect that it will be catch by ".then(...) . but it is not the case
relationType.bind(Bindings.when(Bindings.createBooleanBinding(() -> ( relation == null || relation.get()== null), relation))
.then(RelationType.NEUTRAL)
.otherwise(relation
.get()
.typeProperty()));
All works fine when I add binding to Listener content :
relation.addListener((observable, oldValue, newValue) -> {
if(newValue != null) {
relationType.bind(Bindings
.when(relation.isNull())
.then(RelationType.NEUTRAL)
.otherwise(newValue.typeProperty()));
} else {
relationType.unbind();
relationType.setValue(RelationType.NEUTRAL);
}
});
But I prefere to have only binding. That is possible ??

The problem is the time when relation.get().typeProperty() gets evaluated. It's evaluated when the binding is created and not every time relation changes. You could work around this using a select binding, but you'll receive warnings in the console using this approach:
relationType.bind(Bindings.when(relation.isNull())
.then(RelationType.NEUTRAL)
.otherwise(Bindings.select(relation, "type"));

Related

Default Value for Dropdown

Can't seem to find an answer to this issue easily.
Default values for fields you want to display and allow editing, the best place seems to be to set the widget.value in a draft record, so default date widget.value = new Date(), etc.
How do I reference a dropdown where the dropdown is a related table lookup, e.g., a list of Properties where the bindings are #datasources.Property.Items for the options and #datasource.Item.Property for the value.
If I do console.log(widget.value); I get object Object, which is what I would expect because it is of type object. But how can I set (and retrieve) its value?
Help appreciated.
I tested this for a similar situation in my own application. Do note that there is a minimal delay before the value in the dropdown actually populates. I'm not sure what your alternative solution could be due to the reason stated in my last comment above. I might suggest setting this in your form model onBeforeCreate event instead, but then you would loose the ability to change the relation in the form itself so that might not work for you.
Dropdown widget onAttach event:
var datasource = app.datasources.Property;
if(!datasource.loaded) {
datasource.load(function() {
var index = datasource.items.map(function(i){return i.Status;}).indexOf('Open');
if(index !== -1) {
widget.value = datasource.items[index];
}
});
} else {
var index = datasource.items.map(function(i){return i.Status;}).indexOf('Open');
if(index !== -1) {
widget.value = datasource.items[index];
}
}

Multiple OnSaving event in DevExpress XAF

Im working on a piece of code using DevExpress XAF, I noticed that if im using the event OnSaving that the code executes 2 times, how can i prevent that
protected override void OnSaving()
{
if (PrestamoP != null)
{
PrestamoP.Prestado -= Monto;
PrestamoP.Save();
}
else if (PrestamoG != null)
{
PrestamoG.Prestado -= Monto;
PrestamoG.Save();
}
base.OnSaving();
}
XPO does not guarantee that the OnSaving method is called once. See the corresponding note in the XPO Best Practices article.
I can see that you are changing the PrestamoP.Prestado property based on the value of the Monto property. This code is fine if you execute it only once and only when the Monto property is specified for the first time. This code is not fine if you:
Save this object without changing the Monto property;
Update the early specified Monto value.
So, it appears that a more complex logic is required for the PrestamoG.Prestado property. First, I would move it to the Monto property setter and take the previous value into account (do not forget to check the IsLoading property in this case). Second, I would consider calculating the Prestado value dynamically instead of storing its value. This will allow you to resolve issues with the duplicate business logic execution. See an example here: How to: Calculate a Property Value Based on Values from a Detail Collection.
I can offer different methods for CRUD functions on onSaving method.
IsNewObject, IsDeleted.
// insert
if (Session.IsNewObject(this))
{
a = new a(Session);
a.CreateReferencedProperties(this);
}
// delete
else if (IsDeleted)
{
a= Session.FindObject<A>(PersistentCriteriaEvaluationBehavior.InTransaction, CriteriaOperator.Parse("A=?", this));
if (a!= null)
a.Delete();
}
// update
else
{
a= Session.FindObject<A>(PersistentCriteriaEvaluationBehavior.InTransaction, CriteriaOperator.Parse("A=?", this));
if (a!= null)
a.CreateReferencedProperties(this);
}
You can use the code below to prevent xaf from entering on saving twice.
base.OnSaving();
SessionObjectLayer sessionObjectLayer = this.Session.ObjectLayer as SessionObjectLayer;
if (sessionObjectLayer == null || sessionObjectLayer.ParentSession == null)
{
//Enter only once
}

ILGenerator. Whats wrong with this Code

I am trying to build a dynamic Property Accessor. Want something which is like really fast as close to calling the actually Property. Dont want to go the Reflection route as its very slow. So i opted to using DynamicAssembly and inject IL using ILGenerator. Below is the ILGenerator related code which seems to work
Label nulllabel = getIL.DefineLabel();
Label returnlabel = getIL.DefineLabel();
//_type = targetGetMethod.ReturnType;
if (methods.Count > 0)
{
getIL.DeclareLocal(typeof(object));
getIL.DeclareLocal(typeof(bool));
getIL.Emit(OpCodes.Ldarg_1); //Load the first argument
//(target object)
//Cast to the source type
getIL.Emit(OpCodes.Castclass, this.mTargetType);
//Get the property value
foreach (var methodInfo in methods)
{
getIL.EmitCall(OpCodes.Call, methodInfo, null);
if (methodInfo.ReturnType.IsValueType)
{
getIL.Emit(OpCodes.Box, methodInfo.ReturnType);
//Box if necessary
}
}
getIL.Emit(OpCodes.Stloc_0); //Store it
getIL.Emit(OpCodes.Br_S,returnlabel);
getIL.MarkLabel(nulllabel);
getIL.Emit(OpCodes.Ldnull);
getIL.Emit(OpCodes.Stloc_0);
getIL.MarkLabel(returnlabel);
getIL.Emit(OpCodes.Ldloc_0);
}
else
{
getIL.ThrowException(typeof(MissingMethodException));
}
getIL.Emit(OpCodes.Ret);
So above get the first argument which is the object that contains the property. the methods collection contains the nested property if any. for each property i use EmitCall which puts the the value on the stack and then i try to box it. This works like a charm.
The only issue is if you have a property like Order.Instrument.Symbol.Name and assume that Instrument object is null. Then the code will throw an null object exception.
So this what i did, i introduced a null check
foreach (var methodInfo in methods)
{
getIL.EmitCall(OpCodes.Call, methodInfo, null);
getIL.Emit(OpCodes.Stloc_0);
getIL.Emit(OpCodes.Ldloc_0);
getIL.Emit(OpCodes.Ldnull);
getIL.Emit(OpCodes.Ceq);
getIL.Emit(OpCodes.Stloc_1);
getIL.Emit(OpCodes.Ldloc_1);
getIL.Emit(OpCodes.Brtrue_S, nulllabel);
getIL.Emit(OpCodes.Ldloc_0);
if (methodInfo.ReturnType.IsValueType)
{
getIL.Emit(OpCodes.Box, methodInfo.ReturnType);
//Box if necessary
}
}
Now this code breaks saying That the object/memory is corrupted etc. So what exactly is wrong with this code. Am i missing something here.
Thanks in Advance.
Previously, if you had consecutive properties P returning string and then Q returning int, you would get something like this:
...
call P // returns string
call Q // requires a string on the stack, returns an int
box
...
Now you have something like this:
...
call P // returns string
store // stores to object
... // load, compare to null, etc.
load // loads an *object*
call Q // requires a *string* on the stack
store // stores to object *without boxing*
...
So I see two clear problems:
You are calling methods in such a way that the target is only known to be an object, not a specific type which has that method.
You are not boxing value types before storing them to a local of type object.
These can be solved by reworking your logic slightly. There are also a few other minor details you could clean up:
Rather than ceq followed by brtrue, just use beq.
There's no point in doing Stloc_1 followed by Ldloc_1 rather than just using the value on the stack since that local isn't used anywhere else.
Incorporating these changes, here's what I'd do:
Type finalType = null;
foreach (var methodInfo in methods)
{
finalType = methodInfo.ReturnType;
getIL.EmitCall(OpCodes.Call, methodInfo, null);
if (!finalType.IsValueType)
{
getIL.Emit(OpCodes.Dup);
getIL.Emit(OpCodes.Ldnull);
getIL.Emit(OpCodes.Beq_S, nulllabel);
}
}
if (finalType.IsValueType)
{
getIL.Emit(OpCodes.Box, methodInfo.ReturnType);
//Box if necessary
}
getIL.Emit(OpCodes.Br_S, returnLabel);
getIL.MarkLabel(nulllabel);
getIL.Emit(OpCodes.Pop);
getIL.Emit(OpCodes.Ldnull);
getIL.MarkLabel(returnlabel);
Note that we can get rid of both locals since we now just duplicate the top value on the stack before comparing against null.

Flex DropdownList CreationComplete error

I have a DropdownList that shows a list of providers & the Provider associated with that Patient must be selected.
The Dropdown list:
<s:DropDownList id="providerList"
width="80%"
fontSize="12"
fontWeight="bold"
selectionColor="white"
creationComplete="providerList_creationCompleteHandler(event)"
dataProvider="{model.practiceProviderList.practiceProviders}"/>
where practiceProviders is an ArrayCollection
The CreationCompleteHandler function:
protected function providerList_creationCompleteHandler(event:FlexEvent):void
{
var firstN:String;
var lastN:String;
var providerObj:Provider = new Provider();
if (model.patientDetails.patientDetail.patientProviders != null && model.patientDetails.patientDetail.patientProviders.length > 0)
{
firstN = patientDetailsModel.patientDetails.patientDetail.patientProviders.getItemAt(0).provider.providerName.firstName;
lastN = patientDetailsModel.patientDetails.patientDetail.patientProviders.getItemAt(0).provider.providerName.lastName;
for (var count:int = 0; count < patientDetailsModel.practiceProviderList.practiceProviders.length; ++count)
{
providerObj = patientDetailsModel.practiceProviderList.practiceProviders.getItemAt(count, 0).provider as Provider;
if (providerObj.providerName.firstName == firstN && providerObj.providerName.lastName == lastN)
{
this.providerList.selectedIndex = count;
}
}
}
}
The issue is when I go to this page the first time, the error is :
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at com.newwavetechnologies.modules::demographics/providerList_creationCompleteHandler()[C:\harish\flex\apps\workspace\dataCollection-flexUserInterface\src\com\newwavetechnologies\modules\demographics.mxml:166]
at com.newwavetechnologies.modules::demographics/__providerList_creationComplete()[C:\harish\flex\apps\workspace\dataCollection-flexUserInterface\src\com\newwavetechnologies\modules\demographics.mxml:359]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[E:\dev\4.0.0\frameworks\projects\framework\src\mx\core\UIComponent.as:12266]
at mx.core::UIComponent/set initialized()[E:\dev\4.0.0\frameworks\projects\framework\src\mx\core\UIComponent.as:1577]
at mx.managers::LayoutManager/doPhasedInstantiation()[E:\dev\4.0.0\frameworks\projects\framework\src\mx\managers\LayoutManager.as:759]
at mx.managers::LayoutManager/doPhasedInstantiationCallback()[E:\dev\4.0.0\frameworks\projects\framework\src\mx\managers\LayoutManager.as:1072]
where line 166 is:
if (providerObj.providerName.firstName == firstN && providerObj.providerName.lastName == lastN)
The providerObj is null the first time. But when hit back and come to the same page again, everything works fine and 1 of the providers in the list is selected correctly.
Probably I think the first time, the creationComplete handler method is called before the List is populated. The 2nd time when the call is made, the list is populated and the handler works fine. It would be great if someone can help me in this regard on how to go about this.
Thanks
Harish
It's hard to tell what's going on here, but the problem lies here:
providerObj = patientDetailsModel.practiceProviderList.practiceProviders.getItemAt(count, 0).provider as Provider;
There's a tonne of places in that line that Null pointer exceptions could occur.
Most likely - the practiceProvider returned at position count doesn't have a provider set. We can't see how this value is populated, but given this code works later, I'd say you've got a race condition happening - the data is being accessed before it's been set.
At very least, you should add a guardClause for this:
var practiceProviders:ArrayCollection = patientDetailsModel.practiceProviderList.practiceProviders;
for (var count:int = 0; count < practiceProviders.length; ++count)
{
providerObj = practiceProviders.getItemAt(count, 0).provider as Provider;
if (!providerObj)
continue;
// etc
}
The race condition is a little trickier, given the asyncronous natoure of flex server calls. (I'm assuming that you're loading the data from a remote server).
There's two approaches to solve this - either
defer execution of this method until the data has loaded - you could do this by adding an eventListener to the ResultEvent of the RemoteService
or
Don't worry about it the first time around, but re-execute the method whenever the data changes.
eg:
protected function providerList_creationCompleteHandler(event:FlexEvent):void
{
dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,onCollectionChange,false,0,true);
updateProviders();
// Rest of existing creationComplete code moved to updateProviders();
}
private function updateProviders()
{
// Code from existing creationComplete handler goes here
}
private function onCollectionChange(event:CollectionEvent):void
{
updateProviders();
}

Flex Datagrid Query

Below is part of my code interacting with data grid...!
This lists the children of the particular node if i click on it after refreshing the datagrid..
But if i click on an empty space of the datagrid i get an error saying
"ReferenceError: Error #1069: Property data not found on mx.controls.listClasses.ListBaseContentHolder and there is no default value."
How to avoid that ?
if(event.target.data.children != null)
{
resultSet.removeAll();
var tempChildObj:ArrayCollection;
tempChildObj=event.target.data.children as ArrayCollection;
var childLength:int;
childLength=tempChildObj.length;
for(var i:int =0;i<childLength;i++)
{
resultSet.addItem(tempChildObj.getItemAt(i));
}
resultSet.addItem(tempChildObj);
}
}
What the error is telling you is that the event's target property doesn't have a property called data in your error case. This makes sense since you're clicking on a blank row - a blank row won't contain any data.
What you'll want to do first is check if the event's target contains a data property before you start accessing the child property of data. Either one of the following tests should work:
if( event.target.hasOwnProperty( "data" ) ) {
// rest of your code here
}
or
if( event.target.data ) {
// rest of your code here
}

Resources