TextField.bindAutoCompletion returning object ID - java

I have the following code which build a combobox of states. The code works as expected however I can not get auto complete to work correctly. It appears that the autocomplete is returning object id's instead of the expected text. Clicking on the drop down shows the correct abbreviated state names. Where am I going wrong?
private void buildStateCombo() {
List<StatesDTO> states = GetStateList.getStatesList();
StateCombo.setItems(FXCollections.observableList(states));
StateCombo.setEditable(true);
TextFields.bindAutoCompletion(StateCombo.getEditor(), StateCombo.getItems());
/**
* StringConverter
*/
StateCombo.setConverter(new StringConverter<StatesDTO>() {
#Override
public String toString(StatesDTO object) {
if (object == null){
return null;
} else {
return object.getStateShort();
}
}
#Override
public StatesDTO fromString(String string) {
return DriverAddStateCombo.getItems().stream().filter(state ->
state.getStateShort().equals(string)).findFirst().orElse(null);
}
});
}
GetStateList look like this...
public class GetStateList {
public static List<StatesDTO> getStatesList() {
Database db = new Database();
List<StatesDTO> statesList = new ArrayList<>();
try {
String query = "SELECT stateID, stateCode FROM States";
ResultSet statesRS = db.executeQuery(query);
while(statesRS.next()) {
StatesDTO statesdto = new StatesDTO();
statesdto.setStateID(statesRS.getString(1));
statesdto.setStateShort(statesRS.getString(2));
statesList.add(statesdto);
}
} catch (SQLException ex) {
System.out.println(ex.getMessage());
}
db.closeConnection();
return statesList;
}
}
And StatesDTO
public class StatesDTO {
public SimpleStringProperty stateID = new SimpleStringProperty();
public SimpleStringProperty stateShort = new SimpleStringProperty();
public String getStateID() {
return stateID.get();
}
public void setStateID(String stateIDStr) {
stateID.set(stateIDStr);
}
public String getStateShort() {
return stateShort.get();
}
public void setStateShort(String stateShortStr) {
stateShort.set(stateShortStr);
}
}

One way I could make this work to provide the names directly in a collection.
TextFields.bindAutoCompletion(StateCombo.getEditor(),
StateCombo.getItems().stream().map(state ->
state.getStateShort()).collect(Collectors.toList()) );
I tried different signatures of the method, such as
bindAutoCompletion(TextField textField, Callback> suggestionProvider, StringConverter converter)
but I couldn't make it work.
Note: ControlsFX version 8.40.12.

Related

Global Variable appending multiple copies of data while updating

I have created a class named "Global Services" which I use to save my data globally and access them in a different activity. But when I am calling the set() method, instead of overview the existing data instead it is appending that data. Below is my code.
I have even tried to remove the instance but still, it is appending the new data instead of overwriting.
import java.util.ArrayList;
import java.util.List;
public class GlobalServices {
private static GlobalServices instance;
String partner, leadsResponse;
List<Leads> assignedList = new ArrayList<>();
List<Leads> unAssignedList = new ArrayList<>();
List<Inventory> listInventory = new ArrayList<>();
private GlobalServices() {}
public static GlobalServices getInstance() {
if (instance == null) {
instance = new GlobalServices();
}
return instance;
}
public static void destory() {
instance = null;
}
public String getPartner() {
return partner;
}
public String getLeadsResponse() {
return leadsResponse;
}
public List<Leads> getAssignedList() {
return assignedList;
}
public List<Leads> getUnAssignedList() {
return unAssignedList;
}
public List<Inventory> getListInventory() {
return listInventory;
}
public void setPartner(String partner) {
this.partner = partner;
}
public void setLeadsResponse(String leadsResponse) {
this.leadsResponse = leadsResponse;
}
public void setAssignedList(List<Leads> assignedList) {
this.assignedList = assignedList;
}
public void setUnAssignedList(List<Leads> unAssignedList) {
this.unAssignedList = unAssignedList;
}
public void setListInventory(List<Inventory> listInventory) {
this.listInventory = listInventory;
}
}
The problem is that you're just assigning new references to your lists in GlobalServices but not creating new lists. This means as soon as you modify this reference from another place in your code, it will be reflected in the GlobalServices list as well. All you have to do is:
public void setAssignedList(List<Leads> assignedList) {
this.assignedList = new ArrayList<>(assignedList);
}
public void setUnAssignedList(List<Leads> unAssignedList) {
this.unAssignedList = new ArrayList<>(unAssignedList);
}
public void setListInventory(List<Inventory> listInventory) {
this.listInventory = new ArrayList<>(listInventory);
}
This way a new copy will be created in memory for each list and the data will be overwritten.
Sorry if I was wrong, but your code here is not a problem.
The problem might come from other part of your application.
The data you set might be the data that extend your current data.
Example you have
GlobalServices instance = GlobalServices.getInstance()
List<Inventory> listInventory1 = new ArrayList<>();
listInventory1.add(new Inventory());
instance.setListInventory(listInventory1); // now your inventory have one item
// In some where else in your project
List<Inventory> listInventory2 = instance.getListInventory(); // lisInventorys.size() equals 1
// Then you add more data to listInventory2 by mistake
listInventory2.add(new Inventory()); // listInventory2.size() equals 2
// Then you set back listInventory2 to your global service
instance.setListInventory(listInventory2); // now your inventory have two item
So, the data had been actually overwrite, it data just been extended by accident.

org.primefaces.model.menu.DefaultSubMenu cannot be cast to javax.faces.component.UIComponent

Im trying to upgrade from PrimeFaces 3.5 to PrimeFaces 4.0, the line of code that its fine on version 3.5 is this one:
private MenuModel modelPrincipal;
private Menubar menuBar;
menuBar.getChildren().addAll(modelPrincipal.getContents());
But when I upgrade to version 4.0 I have to change it as follows:
private org.primefaces.model.menu.DefaultMenuModel modelPrincipal;
private Menubar menuBar;
menuBar.getChildren().addAll((Collection<? extends UIComponent>) modelPrincipal.getElements());
and it throws the Exception of title, do you guys know a workaround for it? I couldn't find anything on the documentation of the migration https://github.com/primefaces/primefaces/wiki/Migration-Guide
I also tried:
menuBar.getElements().addAll(modelPrincipal.getElements());
But gives me same Exception
Any help is appreciated
EDIT (Minimal (this is as minimal as I could make the code to make the error appear) Reproducible Example):
This is PrimeFaces 4.0:
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import javax.faces.component.UIComponent;
import org.primefaces.component.menubar.Menubar;
import org.primefaces.model.menu.DefaultMenuItem;
import org.primefaces.model.menu.DefaultSubMenu;
public class MenuExampleMB {
private org.primefaces.model.menu.DefaultMenuModel modelPrincipal;
private Menubar menuBar;
private HashMap<String, SubMenuItemObject> menuUrlCodigos;
public static void main(String[] args) {
MenuExampleMB menuExampleMB = new MenuExampleMB();
// MenuList
List<MenuObject> menuList = new ArrayList<>();
// SubMenuItemList
List<SubMenuItemObject> subMenuList = new ArrayList<>();
// Add 1 item to the SubMenuItemList
subMenuList.add(new SubMenuItemObject("1", "SubMenu", "an url"));
// MenuObject
MenuObject menu1 = new MenuObject("Menu 1");
// Set the SubMenu list for this MenuObject
menu1.setlSubmenus(subMenuList);
menuList.add(menu1);
// Call method that brings the Exception
menuExampleMB.loadMenu(menuList);
}
public void loadMenu(List<MenuObject> lMenus) {
menuUrlCodigos = new HashMap<String, SubMenuItemObject>();
modelPrincipal = new org.primefaces.model.menu.DefaultMenuModel();
menuBar = new Menubar();
// Go through the list of MenuObject and create each SubMenu and add them to
// DefaultMenuModel
for (MenuObject menu : lMenus) {
DefaultSubMenu subMenu = new DefaultSubMenu();
subMenu = agregarSubmenu(menu.getlSubmenus());
subMenu.setLabel(menu.getTitulo());
modelPrincipal.addElement(subMenu);
}
// This is the one that brings the exception.
menuBar.getChildren().addAll((Collection<? extends UIComponent>) modelPrincipal.getElements());
}
private DefaultSubMenu agregarSubmenu(List<SubMenuItemObject> lSubMenuUsuario) {
DefaultMenuItem menuItem = null;
DefaultSubMenu subMenuPadre = new DefaultSubMenu();
for (SubMenuItemObject subMenuItem : lSubMenuUsuario) {
// Ask if this submenu has sons
if (subMenuItem.getlSubmenus().size() > 0) {
DefaultSubMenu subMenuHijo = new DefaultSubMenu();
subMenuHijo.setLabel(subMenuItem.getTitulo());
/*
* Invoke this method with recursivity to get all the sons of this menu
*
*/
subMenuHijo.getElements().addAll(agregarSubmenu(subMenuItem.getlSubmenus()).getElements());
// Add sons to the father submenu.
subMenuPadre.getElements().add(subMenuHijo);
} else {
// This submenu doesn't have sons so its created as an unique son of the
// father submenu.
menuItem = agregarItem(subMenuItem);
subMenuPadre.getElements().add(menuItem);
subMenuPadre.setLabel(subMenuItem.getTitulo());
}
}
return subMenuPadre;
}
private DefaultMenuItem agregarItem(SubMenuItemObject pSubMenuItem) {
DefaultMenuItem menuItem = new DefaultMenuItem();
menuItem.setValue(pSubMenuItem.getTitulo());
menuItem.setUrl(pSubMenuItem.getUrl());
menuUrlCodigos.put(pSubMenuItem.getUrl(), pSubMenuItem);
return menuItem;
}
}
class MenuObject {
private String titulo;
private List<SubMenuItemObject> lSubmenus = new ArrayList<SubMenuItemObject>();
public MenuObject(String pTitulo) {
titulo = pTitulo;
}
public String getTitulo() {
return titulo;
}
public void setTitulo(String titulo) {
this.titulo = titulo;
}
public List<SubMenuItemObject> getlSubmenus() {
return lSubmenus;
}
public void setlSubmenus(List<SubMenuItemObject> lSubmenus) {
this.lSubmenus = lSubmenus;
}
}
class SubMenuItemObject {
private String codigo;
private String titulo;
private String url;
private List<String[]> lJerarquia;
private List<String> lTabs;
private List<SubMenuItemObject> lSubmenus = new ArrayList<SubMenuItemObject>();
public SubMenuItemObject(String pCodigo, String pTitulo, String pUrl) {
codigo = pCodigo;
titulo = pTitulo;
url = pUrl;
lJerarquia = new ArrayList<String[]>();
}
public SubMenuItemObject() {
}
public String getCodigo() {
return codigo;
}
public void setCodigo(String codigo) {
this.codigo = codigo;
}
public String getTitulo() {
return titulo;
}
public void setTitulo(String titulo) {
this.titulo = titulo;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public List<SubMenuItemObject> getlSubmenus() {
return lSubmenus;
}
public void setlSubmenus(List<SubMenuItemObject> lSubmenus) {
this.lSubmenus = lSubmenus;
}
public List<String[]> getlJerarquia() {
return lJerarquia;
}
public void setlJerarquia(List<String[]> lJerarquia) {
this.lJerarquia.clear();
this.lJerarquia.addAll(lJerarquia);
}
public List<String> getlTabs() {
return lTabs;
}
public void setlTabs(List<String> lTabs) {
this.lTabs = lTabs;
}
}
The classcast in
menuBar.getChildren().addAll((Collection<? extends UIComponent>) modelPrincipal.getElements());
is sort of logical since the org.primefaces.model.menu.DefaultMenuItem elements in the model are not in any way a UIComponent (you can see this by following the source up to the base class/interface). Even if you'd not have explicitly casted this it would have failed with the same error but on another line in another class.
Doing the
menuBar.getElements().addAll(modelPrincipal.getElements());
Fails for the same reason. If you'd inspected the code in the getElements() method in the menuBar, you'd have seen
public List getElements() {
MenuModel model = getModel();
if (model != null) {
return model.getElements();
}
else {
return getChildren();
}
}
You could have even debugged it and seen that the model was null and then the getChildren() would be returned, effectively ending up in the same calls as in your first attempt. But...
You'd also see the getModel() in there, maybe being a hint in where to look for the solution. The menuBar has a setModel(...) to where you can set the model you programmatically created. So the solution for this is
menuBar.setModel(modelPrincipal);
IF you create the menuBar programmatically.
But most often it is added in the xhtml via
<p:menu model="#{menuBean.model}" />
Some additional hints:
A search for "primefaces menu model 4.0 3.5" resulted in hits that would have given you help/hints (at least G00gle showed then to me)
typing 'menumodel' in the PrimeFaces 7 documentation would have given a hint to the dynamic menu part in the generic menu component, having a full complete example. https://primefaces.github.io/primefaces/7_0/#/components/menu
Having an IDE with code completion in java code or xhtml and check the api's would have shown the setModel method or the model attribute.
But I agree, a little more explanation in the migration document would have helped, but at the time (when at least I migrated from 3.5 to 4) this was mentioned in the forums etc... so it was 'actual' at the time

Why add method invoked from ActionListener class doesn't add an element to collection? Java

Ok, here's my GUI class (shorted)
public class GUI {
private DataContainer dataContainer;
public GUI(DataContainer dataContainer){
this.dataContainer = dataContainer;
initGUI();
}
class RegisterListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e){
String message;
String firstName = registerPanelFirstNameTextField.getText();
String lastName = registerPanelLastNameTextField.getText();
String login = registerPanelLoginTextField.getName();
String password = registerPanelPasswordTextField.getText();
String adress = registerPanelAdressTextField.getText();
Client client = new Client(firstName, lastName, login, password, adress);
boolean registerCheck = dataContainer.registerClient(client);
if (registerCheck) {
message = "SUCCES!";
} else {
message = "FAILURE!";
}
JOptionPane.showMessageDialog(new JFrame(), message);
}
}
And here's my DataContainer class (also shorted):
public class DataContainer implements Subject {
public boolean workingStatus = true;
private List<Client> clientList = new ArrayList<>();
private List<Auction> auctionList = new ArrayList<>();
private List<Observer> observersList = new ArrayList<>();
public boolean registerClient(Client client) {
String testLogin = client.getLogin();
boolean isClientOnList = isClientOnList(testLogin);
if (isClientOnList) {
return false;
} else {
addClientToList(client);
return true;
}
}
private void addClientToList(Client client) {
clientList.add(client);
System.out.println(clientList);
}
And here's my question - why method invoked from register listener gives empty collection. SysOut prints [null]. I have used JUnit and reflection to directly test addClientToList(), and it works, but it simply doesn't when clicking the button. And yes, in my class I created new DataContainer object, and passed it to GUI constructor.
Oh RIGHT! Found this little guy. Problem was here:
String login = registerPanelLoginTextField.getName();
and it should be:
String login = registerPanelLoginTextField.getText();

TableView doesn't refresh

I've got a project written in JavaFX and I'm trying to get a refresh on a tableview without result.
I've googled around and tried some examples I've found but it still doesn't work.
I populate a tableview with information each row in this table can have new comments added to by double click on the row. The a new Tabpane is opened and the new comment can be added there. On close of this tabpane I'd like the one I clicked from to be refreshed.
I must be doing something wrong. I just don't know what.
In my StoreController
private void populateTableView(List<Store> stores) {
ObservableList<Store> data = FXCollections.observableArrayList(stores);
storeNumberColumn.setCellValueFactory(
new PropertyValueFactory<Store, String>("id"));
storePhoneColumn.setCellValueFactory(
new PropertyValueFactory<Store, String>("phoneNbr"));
chainColumn.setCellValueFactory(
new PropertyValueFactory<Store, String>("chainId"));
commentColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Store, ImageView>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Store, ImageView> p) {
Integer numberOfComments = p.getValue().getCommentsCount();
ReadOnlyObjectWrapper wrapper = null;
if (numberOfComments == 0) {
wrapper = null;
} else if (numberOfComments == 1) {
wrapper = new ReadOnlyObjectWrapper(new ImageView(COMMENT_SINGLE_FLAG_SOURCE));
} else {
wrapper = new ReadOnlyObjectWrapper(new ImageView(COMMENT_DOUBLE_FLAG_SOURCE));
}
return wrapper;
}
});
storeTable.setItems(data);
sortTable(storeTable, missedColumn);
}
#FXML
public void handleTableAction(MouseEvent event) {
if (event.getClickCount() == 2) {
showNewCommentStage();
}
}
private void showNewCommentStage() {
initCommentController();
Store store
= storeTable.getSelectionModel().selectedItemProperty().getValue();
commentController.showNewStage(commentPane, store);
}
It seems like the call-function doesn't get called when the commentpane is closed.
CommentController
public void showNewStage(Pane pane, Store store) {
this.store = store;
initStage(pane);
windowHandler = new WindowHandler(stage);
effectHandler.playEffect(pane);
constructCommentHeaders();
List<Comment> comments;
comments = commentService.listByStoreId(store.getId());
populateCommentTable(comments);
}
Like I said I've tried a lot of the solutions found here on Stackoverflow but with no results. The Tableview doesn't refresh. The Stores and the Comments are in different database tables if that's important
Can someone point me in the right direction?
Thanks!
****EDIT****
The Store.class
public class Store extends CommentEntity {
private String id;
private String chainId;
private String phoneNbr;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getChainId() {
return chainId;
}
public void setChainId(String chainId) {
this.chainId = chainId;
}
public String getPhoneNbr() {
return phoneNbr;
}
public void setPhoneNbr(String phoneNbr) {
this.phoneNbr = phoneNbr;
}
#Override
public String toString() {
return "Store{" + "id=" + id + ", chainId=" + chainId + '}';
}
#Override
public String getCommentIdentifier() {
return id;
}
}
The CommentEntity.Class
public abstract class CommentEntity {
private int commentsCount;
public int getCommentsCount() {
return commentsCount;
}
public void setCommentsCount(int commentsCount) {
this.commentsCount = commentsCount;
}
public abstract String getCommentIdentifier();
}
Thank you for input, I hadn't even reflected over the ImageView / String.
Two issues:
First, you need to distinguish between the data the cells in your column are displaying, and the cells that actually display those data. The cellValueFactory determines the data that are displayed. The PropertyValueFactory is a cellValueFactory implementation that references a JavaFX Property, so when you call
storeNumberColumn.setCellValueFactory(new PropertyValueFactory<Store, String>("id"));
it effectively tells the cells in the storeNumberColumn to call the idProperty() method on the Store object in the current row to get the data for the cell. (If no such method exists, it will try to use getId() as a backup plan.)
By default, you get a cellFactory that displays text resulting from calling toString() on the data generated by the cellValueFactory. In the case where your data are simply Strings, this is usually what you need. In other cases, you often need to provide a cellFactory of your own to get the correct way to display the data.
In your case, the data for the commentColumn are simply the number of comments. You are going to display that by choosing an image based on that numeric value.
So you should have
TableColumn<Store, Number> commentColumn = new TableColumn<>("Comments");
For the cellValueFactory, you can just use
commentColumn.setCellValueFactory(new PropertyValueFactory<>("commentsCount"));
Then you need a cellFactory that displays the appropriate ImageView:
commentColumn.setCellFactory(new Callback<TableColumn<Store, Number>, new TableCell<Store, Number>>() {
#Override
public TableCell<Store, Number>() {
private ImageView imageView = new ImageView();
#Override
public void updateItem(Number numberOfComments, boolean empty) {
super.updateItem(count, empty) ;
if (empty) {
setGraphic(null);
} else {
if (numberOfComments.intValue() == 0) {
setGraphic(null);
} else if (numberOfComments.intValue() == 1) {
imageView.setImage(new Image(COMMENT_SINGLE_FLAG_SOURCE));
setGraphic(imageView);
} else {
imageView.setImage(new Image(COMMENT_DOUBLE_FLAG_SOURCE));
setGraphic(imageView);
}
}
}
}
});
The second issue is actually about the update. A TableView keeps its contents "live" by observing JavaFX properties that are provided by the cellValueFactory as ObservableValues. If the value might change while the table is displayed, you must provide an actual property that can be observed: using a ReadOnlyObjectWrapper is no good (because it's read only, so it's wrapped value will not change). The PropertyValueFactory will also return a ReadOnlyObjectWrapper if you do not have JavaFX property accessor methods (i.e. if it is only using getXXX() methods to access the data). So your model class must provide JavaFX Properties.
You can make an immediate fix to this by updating CommentEntity to use an IntegerProperty:
public abstract class CommentEntity {
private final IntegerProperty commentsCount = new SimpleIntegerProperty();
public final int getCommentsCount() {
return commentsCountProperty().get();
}
public final void setCommentsCount(int commentsCount) {
commentsCountProperty().set(commentsCount);
}
public IntegerProperty commensCountProperty() {
return commentsCount ;
}
public abstract String getCommentIdentifier();
}
I would also strongly recommend updating the Store class to use JavaFX Properties in a similar manner.

Unexpected slowness in Play framework project with Mongodb and Morphia

I am building a RSS reader using the Play Framework, play authenticate, and mongodb accessed through Morphia. It's running on a Ubuntu server with a VIA Nano processor U2250 (1.6GHz Capable) and 2ghz ram apparently. Despite every tweak and optimization I could think of, I still observe insane delays in the answering time: from 2seconds to 1 minute to answer a simple HTTP GET request to mark an item as read (I'll talk about this example because it is simple). Note that i am not facing this problem on local tests on my personal machine, just on remote deployment.
Profiling gives me the following distribution of spent time to answer a dozen "mark as read" requests :
org.bson.io.PoolOutputBuffer.pipe() 11%
org.bson.io.Bits.readFully() 10%
scala.concurrent.forkjoin.ForkJoinPool.scan() 8%
org.jboss.netty.channel.socket.nio.SelectorUtil.select() 7%
org.bson.io.PoolOutputBuffer.write 5%
com.google.code.morphia.mapping.DefaultCreator.getNoArgsConstructor() 5%
from which I gathered the I/O with the database was the bottleneck. I have made the references explicit using a custom class Ref<> because Key<> was handling package names fairly poorly. Therefore the aforementioned mark as read request only does 3 db queries : retrieve the item by its ref (id), retrieve the inbox by its ref (id) stored in the session cache, remove the item from the inbox and store it back again. My interactions with the database are encapsulated in a static helper class :
public class MorphiaHelper {
static private Mongo mongo;
static private Morphia morphia;
static private Datastore datastore;
public static void mapModels() {
morphia.map(...);
}
public static void startup() {
try {
mongo = new MongoClient();
morphia = new Morphia();
datastore = MorphiaHelper.morphia.createDatastore(MorphiaHelper.mongo, "dbname");
mapModels();
updateModelsIfNeeded();
datastore.ensureIndexes();
datastore.ensureCaps();
} catch (Exception e) {
Logger.error("Database failed to initialize:" + e.getMessage());
}
Logger.debug("** mongo and morphia initialization on datastore: " + MorphiaHelper.datastore.getDB());
}
public static void shutdown() {
mongo.close();
Logger.debug("** mongo and morphia closed **");
}
public static <T> Key<T> save(T entity){
return datastore.save(entity);
}
public static <T, V> T get(Ref<V> ref) throws NotFoundException{
try {
Class<T> classT = (Class<T>)Class.forName(ref.getClassName());
return datastore.get(classT, ref.getId());
} catch (ClassNotFoundException e) {
throw new NotFoundException("Reference to morphia not found:" + ref.toString());
}
}
...
}
and rely on the aforementionned inner static Ref class :
public static interface Referencable{
Ref getRef();
}
public static class Ref<T> implements Serializable, Comparable {
Map<String, String> id;
private static String nameToKey(String name){
return name.replace(".", " ");
}
private static String keyToName(String key){
return key.replace(" ", ".");
}
public static <T> Ref<T> fromIdString(Class<T> className, String idString) {
return new Ref<T>(className, idString);
}
public static Ref<?> fromString(String sourceRefString) throws ClassNotFoundException {
if (! sourceRefString.contains("=")){
throw new ClassNotFoundException();
}
String[] parseString = sourceRefString.split("=", 2);
Class<?> className = Class.forName(keyToName(parseString[0]));
return fromIdString(className, parseString[1]);
}
#Deprecated
private Ref() {}
public Ref(Class<T> _className, String _id) {
id = Collections.singletonMap(nameToKey(_className.getName()), _id);
}
private Entry<String, String> getIdFromMap(){
return id.entrySet().iterator().next();
}
public Object getId(){
return getIdFromMap().getValue();
}
public String toString(){
return getIdFromMap().toString();
}
#Override
public boolean equals(Object other){
return (other.getClass().equals(this.getClass())
&& ((Ref)other).getIdFromMap().getKey().equals(this.getIdFromMap().getKey())
&& ((Ref)other).getIdFromMap().getValue().equals(this.getIdFromMap().getValue()));
}
#Override
public int hashCode() {
return getIdFromMap().hashCode();
}
public String getClassName() {
return keyToName(getIdFromMap().getKey());
}
#Override
public int compareTo(Object arg0) {
return this.toString().compareTo(((Ref) arg0).toString());
}
}
Since I haven't been able to clearly locate the problem, I am also offering you just in case the code of the controller :
public static Result read(final String id) throws NotFoundException{
Item item = MorphiaHelper.get(Ref.fromIdString(Item.class, id));
Application.getLocalInbox().remove(item);
return ok();
}
This is Application, getting us the inbox :
public class Application extends Controller {
public static Inbox getLocalInbox() throws NotFoundException {
return Inbox.getInboxOfUser(getLocalUserRef());
}
public static User getLocalUser() throws NotFoundException {
User user = UserDAO.findUser(PlayAuthenticate.getUser(session()));
session("UserRef", user.getRef().toString());
return user;
}
public static Ref<User> getLocalUserRef() throws NotFoundException {
if (session("UserRef") == null){
session("UserRef", getLocalUser().getRef().toString());
}
try {
return (Ref<User>) Ref.fromString(session("UserRef"));
} catch (Exception e){
throw new NotFoundException("could not retrieve current user ref");
}
}
}
So yeah nothing very interesting here. This is inbox.remove :
public void remove(Item item) {
// TODO can be optimized if we dont check the existence of tags
boolean listContainedElement = false;
for(Ref<Tag> tag : item.getTagsRef()){
List<Ref<Item>> list = inbox().get(tag);
if (list != null){
Ref<Item> key = item.getRef();
boolean remove = list.remove(key);
listContainedElement = listContainedElement || remove;
if (list.size() == 0){
inbox().remove(tag);
}
}
}
List<Ref<Item>> list = inbox.get(Tag.getUntagged());
if (list != null){
listContainedElement = listContainedElement || list.remove(item.getRef());
}
if (listContainedElement) seen(item);
MorphiaHelper.save(this);
}
where
Map<String, List<Ref<Item>>> inbox = new HashMap<String, List<Ref<Item>>>();
Map<Ref<Tag>, List<Ref<Item>>> inbox(){
return new MorphiaMap(inbox);
}
is a way to bypass the fact that morphia cannot handle anything else than Strings as map keys and keep code clean, MorphiaMap being a class storing an attribute "Map innerMap;" and forwarding all the commands with the String to Ref translation.
I'm completely at a loss here so any kind of advice would be much appreciated. I've been tweaking the system for weeks to improve performances but I'm afraid I'm missing the elephant in the room.
Thank you in advance
Best regards

Categories