Property's setLabel cause NPE - codenameone

I'm trying the BoundTableModel for the first time, and when I tried to set the labels of my properties, I've got a NullPointerException.
[EDT] 0:0:8,239 - Exception: java.lang.NullPointerException - null
java.lang.NullPointerException
at com.codename1.properties.PropertyBase.putClientProperty(PropertyBase.java:131)
at com.codename1.properties.PropertyBase.setLabel(PropertyBase.java:192)
at our.app.forms.UnitsForm.<init>(UnitsForm.java:54)
The NPE is throwed in this code block, but I don't understand why. The properties are public final and instanciated within the UserHistory, but no label are set there. It's better to set them later, as they need to be translated by our TranslationManager.
UiBinding ui = new UiBinding();
List<UserHistory> histories = RestManager.getHistory();
UserHistory prototype = new UserHistory();
prototype.dateCreate.setLabel("Date");
prototype.balanceUpdate.setLabel("Variation");
prototype.action.setLabel("Action");
UiBinding.BoundTableModel tb = ui.createTableModel(histories, prototype);
tb.setColumnOrder(prototype.dateCreate, prototype.balanceUpdate, prototype.action);
Table table = new Table(tb);
I don't understand how the setLabel(String) of Property can throw a NullPointerException, any idea ?
EDIT: Here are the classes which fails, the first is the AbstractEntity:
public abstract class AbstractEntity implements Serializable, PropertyBusinessObject
{
public final LongProperty<AbstractEntity> id = new LongProperty<>("id");
public final IntProperty<AbstractEntity> version = new IntProperty<>("version");
public final Property<Date, AbstractEntity> dateCreate = new Property<>("dateCreate", Date.class);
public final Property<Date, AbstractEntity> dateUpdate = new Property<>("dateUpdate", Date.class);
protected List<PropertyBase> getPropertyList()
{
List<PropertyBase> list = new ArrayList<>();
list.add(id);
list.add(version);
list.add(dateCreate);
list.add(dateUpdate);
return list;
}
protected List<PropertyBase> getExcludePropertyList()
{
List<PropertyBase> list = new ArrayList<>();
return list;
}
#Override
public PropertyIndex getPropertyIndex()
{
PropertyBase[] properties = getPropertyList().toArray(new PropertyBase[getPropertyList().size()]);
PropertyIndex index = new PropertyIndex(this, getName(), properties);
for(PropertyBase excluded : getExcludePropertyList())
{
index.setExcludeFromJSON(excluded, true);
index.setExcludeFromMap(excluded, true);
}
return index;
}
}
And here is the child class which I tried to show in a table:
public class UserHistory extends AbstractEntity
{
public final Property<User, UserHistory> user = new Property<>("user", User.class);
public final DoubleProperty<UserHistory> balanceUpdate = new DoubleProperty<>("balanceUpdate");
public final Property<String, UserHistory> action = new Property<>("action");
public UserHistory() {}
#SuppressWarnings("rawtypes")
protected List<PropertyBase> getPropertyList()
{
List<PropertyBase> list = super.getPropertyList();
list.add(user);
list.add(balanceUpdate);
list.add(action);
return list;
}
}

This won't work.
Your approach is interesting but it behaves differently from the common implementation in one significant way: the index is created lazily. So initialization work done by the index doesn't actually happen in all cases.
Worse, you create multiple indexes every time the get method is called which is also problematic.
Something that "might" work is this:
public abstract class AbstractEntity implements Serializable, PropertyBusinessObject
{
public final LongProperty<AbstractEntity> id = new LongProperty<>("id");
public final IntProperty<AbstractEntity> version = new IntProperty<>("version");
public final Property<Date, AbstractEntity> dateCreate = new Property<>("dateCreate", Date.class);
public final Property<Date, AbstractEntity> dateUpdate = new Property<>("dateUpdate", Date.class);
private PropertyIndex index;
protected AbstractEntity() {
getPropertyIndex();
}
protected List<PropertyBase> getPropertyList()
{
List<PropertyBase> list = new ArrayList<>();
list.add(id);
list.add(version);
list.add(dateCreate);
list.add(dateUpdate);
return list;
}
protected List<PropertyBase> getExcludePropertyList()
{
List<PropertyBase> list = new ArrayList<>();
return list;
}
#Override
public PropertyIndex getPropertyIndex()
{
if(idx == null) {
PropertyBase[] properties = getPropertyList().toArray(new PropertyBase[getPropertyList().size()]);
index = new PropertyIndex(this, getName(), properties);
for(PropertyBase excluded : getExcludePropertyList())
{
index.setExcludeFromJSON(excluded, true);
index.setExcludeFromMap(excluded, true);
}
}
return index;
}
}
Original answer below:
This happens if you forgot to add the property to the index object. In that case the parent object is null and meta-data can't be set.

Related

Store to JSON a PropertyBusinessObject containing a ListProperty

I have difficult to store to JSON a PropertyBusinessObject containing a ListProperty. I created the following test case, that works fine on the first execution, but fails on the second. Is my code wrong?
My test case if composed by four classes:
TestCaseJSON
public void init(Object context) {
[...]
DB.initPersistenceDB();
}
public void start() {
if(current != null){
current.show();
return;
}
Form hi = new Form("Hi World", BoxLayout.y());
hi.add(new Label("Hi World"));
hi.show();
DB.userDB.uniqueID.set("1");
DB.userDB.authToken.set("myToken");
InjuryDAO newInjury = new InjuryDAO();
DB.userDB.injuries.add(newInjury);
DB.saveDB();
}
DB
public class DB {
// User Database
public static UserDB userDB = new UserDB();
/**
* Call this method in the init(), it allows the database persistence and
* automatically restores the saved DB.
*/
public static void initPersistenceDB() {
Log.p("DB.initPersistenceDB executing", Log.DEBUG);
restoreDB();
}
/**
* Saves the UserDB to the Storage
*/
public static void saveDB() {
Log.p("DB.saveUserDB executing", Log.INFO);
userDB.getPropertyIndex().storeJSON("UserDB");
}
/**
* Restore the UserDB from the Storage, if it was previously saved to; this
* method is called by initPersistenceDB()
*/
private static void restoreDB() {
Log.p("DB.restoreUserDB executing", Log.INFO);
if (Storage.getInstance().exists("UserDB")) {
userDB.getPropertyIndex().loadJSON("UserDB");
}
}
}
UserDB
public class UserDB implements PropertyBusinessObject {
public final Property<String, UserDB> uniqueID = new Property<>("uniqueID", null);
public final Property<String, UserDB> authToken = new Property<>("authToken", null);
public final ListProperty<InjuryDAO, UserDB> injuries = new ListProperty<>("injuries");
public final PropertyIndex idx = new PropertyIndex(this, "UserDB",
uniqueID, authToken,
injuries
);
#Override
public PropertyIndex getPropertyIndex() {
return idx;
}
}
InjuryDAO
public class InjuryDAO implements PropertyBusinessObject {
// extra info
public final LongProperty<InjuryDAO> injuryCreationTime = new LongProperty<>("injuryCreationTime", System.currentTimeMillis());
// mandatory info
public final Property<Date, InjuryDAO> dateInjury = new Property<>("dateInjury", Date.class);
private final PropertyIndex idx = new PropertyIndex(this, "InjuryDAO",
injuryCreationTime, dateInjury
);
#Override
public PropertyIndex getPropertyIndex() {
return idx;
}
}
Before executing in the Simulator, I cleared the .cn1 directory (that is equivalent to Simulator, Clean Storage).
After the first execution, this is the log:
[EDT] 0:0:0,110 - DB.initPersistenceDB executing
[EDT] 0:0:0,110 - DB.restoreUserDB executing
[EDT] 0:0:0,178 - DB.saveUserDB executing
and this is the content of the UserDB file inside the .cn1 directory:
{
"injuries": [{"injuryCreationTime": 1558202520667}],
"authToken": "myToken",
"uniqueID": "1"
}
That's correct.
After the second execution, the log is identical to the previous one, but this is the wrong content of the UserDB file (note that the previous time stamp is lost):
{
"injuries": [
{"injuries": []},
{"injuryCreationTime": 1558202625655}
],
"authToken": "myToken",
"uniqueID": "1"
}
The expected correct result should be:
{
"injuries": [
{"injuryCreationTime": 1558202520667},
{"injuryCreationTime": 1558202625655}
],
"authToken": "myToken",
"uniqueID": "1"
}
Thank you for your support.
I think this:
public final ListProperty<InjuryDAO, UserDB> injuries = new ListProperty<>("injuries");
Should be changed to:
public final ListProperty<InjuryDAO, UserDB> injuries = new ListProperty<>("injuries", InjuryDAO.class);
If this doesn't work try:
public final ListProperty<InjuryDAO, UserDB> injuries = new ListProperty<>("injuries", InjuryDAO.class, null);

Virtualizing DataGrid over ListCollectionView

I am using a ListCollectionView over a custom list which provides read access to a particular database table. Below is the definition of the custom list.
class MaterialCollection : IList
{
#region Fields
private Object syncRoot;
private SQLiteConnection connection;
#endregion
#region IList Interface
public object this[int index]
{
get
{
using (SQLiteCommand command = connection.CreateCommand())
{
command.CommandText = "SELECT * FROM Materials LIMIT 1 OFFSET #Index";
command.Parameters.AddWithValue("#Index", index);
using (SQLiteDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
return GetMaterial(reader);
}
else
{
throw new Exception();
}
}
}
}
set
{
throw new NotImplementedException();
}
}
public int Count
{
get
{
using (SQLiteCommand command = connection.CreateCommand())
{
command.CommandText = "SELECT Count(*) FROM Materials";
return Convert.ToInt32(command.ExecuteScalar());
}
}
}
public bool IsFixedSize
{
get
{
return false;
}
}
public bool IsReadOnly
{
get
{
return true;
}
}
public bool IsSynchronized
{
get
{
return true;
}
}
public object SyncRoot
{
get
{
return this.syncRoot;
}
}
public IEnumerator GetEnumerator()
{
return Enumerate().GetEnumerator();
}
#endregion
#region Constructor
public MaterialCollection(SQLiteConnection connection)
{
this.connection = connection;
this.syncRoot = new Object();
}
#endregion
#region Private Methods
private Material GetMaterial(SQLiteDataReader reader)
{
int id = Convert.ToInt32(reader["Id"]);
string materialNumber = Convert.ToString(reader["MaterialNumber"]);
string type = Convert.ToString(reader["Type"]);
string description = Convert.ToString(reader["Description"]);
string alternateDescription = Convert.ToString(reader["AlternateDescription"]);
string tags = Convert.ToString(reader["Tags"]);
Material material = new Material();
material.Id = id;
material.MaterialNumber = materialNumber;
material.Type = (MaterialType)Enum.Parse(typeof(MaterialType), type);
material.Description = description;
material.AlternateDescription = alternateDescription;
material.Tags = tags;
return material;
}
private IEnumerable<Material> Enumerate()
{
using (SQLiteCommand command = connection.CreateCommand())
{
command.CommandText = "SELECT * FROM Materials";
using (SQLiteDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
yield return GetMaterial(reader);
}
}
}
}
#endregion
#region Unimplemented Functions
public int Add(object value)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(object value)
{
throw new NotImplementedException();
}
public void CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
public int IndexOf(object value)
{
return 0;
}
public void Insert(int index, object value)
{
throw new NotImplementedException();
}
public void Remove(object value)
{
throw new NotImplementedException();
}
public void RemoveAt(int index)
{
throw new NotImplementedException();
}
#endregion
}
The problem is that during the initial population of the DataGrid, the this[int index] method is called for every single row (i.e. 0 through Count - 1).
As the table contains potentially millions of rows and read operations are costly (i.e. SQLite on a legacy HDD), such an operation is unacceptable. Furthermore, it doesn't make sense to load all of the millions of rows when the screen can only accommodate for 50 or so items.
I would like to make it such that the DataGrid fetches only the row that it is displaying, instead of fetching the entire table. How is such a working possible?
(P.S. I have ensured that virtualizatin is enabled for the DataGrid itself)
It seems that the problem was resolved after adding ScrollViewer.CanContentScroll="True" to the ListView.

to display the data obtained from button listener to list- codename one

I have a button that display the data obtained from json. below is my code for button action. I need help to display the data obtained to list.
#Override
protected void onMain_ButtonAction(final Component c, ActionEvent event) {
ConnectionRequest r = new ConnectionRequest() {
Hashtable h;
#Override
protected void postResponse() {
}
#Override
protected void readResponse(InputStream input) throws IOException {
InputStreamReader reader = new InputStreamReader(input);
JSONParser p = new JSONParser();
h = p.parse(new InputStreamReader(input));
Hashtable response = p.parse(reader);
Hashtable feed = (Hashtable)response.get("root");
for (Object s : h.values()) {
Vector vec = new Vector(100);
vec = (Vector)s;
int i;
for(i = 0; i<vec.size(); i++){
Hashtable<String, String> ht= (Hashtable<String, String>) vec.get(i);
System.out.println(ht.get("location"));
// findLabel().setText(ht.get("location"));
}
}
}
};
r.setUrl("http://ruslanapp.demo.capitaleyenepal.com/vodka-mobile-interface/getData/locations");
r.setPost(false);
InfiniteProgress prog = new InfiniteProgress();
Dialog dlg = prog.showInifiniteBlocking();
r.setDisposeOnCompletion(dlg);
NetworkManager.getInstance().addToQueue(r);
}
I want to list the data obtained frm btn above to the list below. how can I do it??
#Override
protected boolean initListModelList1(List cmp) {
cmp.setModel(new com.codename1.ui.list.DefaultListModel(new String[] {"Item 1", "Item 2", "Item 3"}));
return true;
}
You did most of the work well, I suggest avoiding a list and using an infinite container. The PropertyCross demo has pretty much this functionality (including JSON): https://www.udemy.com/learn-mobile-programming-by-example-with-codename-one/
To finish the code above create the model ArrayList above e.g. assuming you are using a MultiList:
// define this in the class variables:
private ArrayList<Map<String, String>> modelData = new ArrayList<Map<String, String>>();
// then in the code (I assumed stuff about your JSON, correct the
// code to extract the data correctly, just set the hashmap values
for (Object s : h.values()) {
Collection<Map<String, String>>) data = (Collection<Map<String, String>>))s;
for(Map<String, String> ht : data) {
String location = ht.get("location");
HashMap<String, String> entry = new HashMap<String, String>();
entry.put("Line1", location);
modelData.add(entry);
}
}
Then in:
#Override
protected boolean initListModelList1(List cmp) {
cmp.setModel(new DefaultListModel(modelData));
return true;
}
Notice that you should use showForm() to show the next form in the postResponse method.

Passing a list or array to RESTeasy using get

I've seen this kind of thing described in various examples showing how to create a REST service which takes arrays or a list of objects as part of the URL.
My question is, how to implement this using RESTeasy?
Something like the following would be how i would assume this to work.
#GET
#Path("/stuff/")
#Produces("application/json")
public StuffResponse getStuffByThings(
#QueryParam("things") List<Thing> things);
Create a StringConverter and a use a wrapper object. Here is a quick and dirty example:
public class QueryParamAsListTest {
public static class Thing {
String value;
Thing(String value){ this.value = value; }
}
public static class ManyThings {
List<Thing> things = new ArrayList<Thing>();
ManyThings(String values){
for(String value : values.split(",")){
things.add(new Thing(value));
}
}
}
static class Converter implements StringConverter<ManyThings> {
public ManyThings fromString(String str) {
return new ManyThings(str);
}
public String toString(ManyThings value) {
//TODO: implement
return value.toString();
}
}
#Path("/")
public static class Service {
#GET
#Path("/stuff/")
public int getStuffByThings(
#QueryParam("things") ManyThings things){
return things.things.size();
}
}
#Test
public void test() throws Exception {
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getProviderFactory().addStringConverter(new Converter());
dispatcher.getRegistry().addSingletonResource(new Service());
MockHttpRequest request = MockHttpRequest.get("/stuff?things=a,b,c");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
Assert.assertEquals("3", response.getContentAsString());
}
}
I think you can also use a StringParamUnmarshaller
I had some luck with this, using Collection rather than List. I was unable to make a StringConverter for List work.
#Provider
public class CollectionConverter implements StringConverter<Collection<String>> {
public Collection<String> fromString(String string) {
if (string == null) {
return Collections.emptyList();
}
return Arrays.asList(string.split(","));
}
public String toString(Collection<String> values) {
final StringBuilder sb = new StringBuilder();
boolean first = true;
for (String value : values) {
if (first) {
first = false;
} else {
sb.append(",");
}
sb.append(value);
}
return sb.toString();
}
}
I did the toString from my head. Be sure to write unit tests for it to verify. But of course, everything is easier and clearer when you use Guava. Can use Joiner and Splitter. Really handy.
Just use a wrapper on its own, no need for anything else.
In your endpoint
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Path("/find")
#GET
MyResponse find(#QueryParam("ids") Wrapper ids);
And you wrapper looks like this :
public class Wrapper implements Serializable {
private List<BigInteger> ids = Collections.emptyList();
public String toString() {
return Joiner.on(",")
.join(ids);
}
public List<BigInteger> get() {
return ids;
}
public Wrapper(String s) {
if (s == null) {
ids = Collections.emptyList();
}
Iterable<String> splitted = Splitter.on(',')
.split(s);
Iterable<BigInteger> ids = Iterables.transform(splitted, Functionz.stringToBigInteger);
this.ids = Lists.newArrayList(ids);
}
public Wrapper(List<BigInteger> ids) {
this.ids = ids;
}
}

Why can't I add attributes when serializing ObservableCollection<T>?

I'm trying to extend an ObservableCollection with a few custom properties and have it serialize. However, I can't seem to get it to serialize these properties. I'm using .NET 4.0 where they fixed the serialization issues of ObservableCollection, but am still having problems. My hunch is that GetObjectData is being called on the base class and not mine. Any ideas?
[Serializable]
[XmlRoot(ElementName = "MyCollection")]
public class MyCollection : ObservableCollection<MyItem>, ISerializable
{
private string name;
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Name", Name);
}
private MyCollection()
{
Name = string.Empty;
}
public MyCollection(string name)
{
Name = name;
}
public MyCollection(SerializationInfo info, StreamingContext context)
{
Name = (string)info.GetValue("Name", typeof(string));
}
[XmlAttribute]
public string Name
{
get { return name; }
protected set
{
string originalName = name;
name = value;
if (originalName != name)
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
public void SaveToFile(string path)
{
string directory = Path.GetDirectoryName(path);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
XmlSerializer serializer = new XmlSerializer(typeof(MyCollection));
using (TextWriter textWriter = new StreamWriter(path))
{
serializer.Serialize(textWriter, this);
textWriter.Close();
}
}
public static MyCollection LoadFromFile(string path)
{
XmlSerializer deserializer = new XmlSerializer(typeof(MyCollection));
using (TextReader textReader = new StreamReader(path))
{
MyCollection myCollection = (MyCollection)deserializer.Deserialize(textReader);
textReader.Close();
return myCollection;
}
}
}
XML Serialization does not support this scenario. You simply cannot add anything to a class implementing ICollection.
If you require this, then you will have to implement IXmlSerializable and do the work yourself.
Note that you may be confusing XML Serialization with runtime serialization. XML Serialization doesn't care about the [Serializable] attribute or GetObjectData, etc.

Resources