gwtgwt-editors

ListEditor with polymorphic types


I have searched around and been trying to figure out if I can use the editor framework with polymorphic types. I found this post at Using GWT Editors with a complex usecase which is close to what I am trying to do. I am fairly new to the editor framework, so any help would be much appreciated.

For example, here is some of the code,

Data Transfer Objects:

public class Employee{ 
 public List<Contact> contacts 
}
public class Contact {
public class ContactEmail extends Contact {}
public class ContactAddress extends Contact {}
public class ContactPhoneNumber extends Contact {}

Editors:

public interface ContactBaseEditor<T extends Contact> extends Editor<T> {}
public class AddressEditor extends Composite implements Editor<ContactAddress>, ContactBaseEditor<ContactAddress>{}
public class EmailEditor extends Composite implements Editor<ContactEmail>, ContactBaseEditor<ContactEmail>{)
public class PhoneNumberEditor extends Composite implements Editor<ContactPhoneNumber>, ContactBaseEditor<ContactPhoneNumber>{}

ContactEditor class:

public class ContactEditor extends Composite implements IsEditor<ListEditor<Contact, ContactEditorWrapper>> {
private class ContactEditorSource extends EditorSource<ContactEditorWrapper> {
    @Override
    public ContactEditorWrapper create(final int index) {
        ContactEditorWrapper contactEditor = new ContactEditorWrapper();
        communicationContactsPanel.add(contactEditor);
        return contactEditor;
    }
    @Override
    public void dispose(ContactEditorWrapper subEditor) {
        subEditor.removeFromParent();   
        }
    @Override
    public void setIndex(ContactEditorWrapper editor, int index) {
        communicationContactsPanel.insert(editor, index);
    }
}

private ListEditor<Contact, ContactEditorWrapper> editor = ListEditor.of(new ContactEditorSource());
public ListEditor<Contact, ContactEditorWrapper> asEditor() {
    return editor;
}
}

ContactEditorWrapper:

class ContactEditorWrapper extends Composite implements ContactBaseEditor<Contact>, ValueAwareEditor<Contact> {

   private SimplePanel panel = new SimplePanel();

   @Path("") ContactBaseEditor<Contact> realEditor;

   public ContactEditor() {
       initWidget(panel);
   }
   @Override
   public void setValue(Contact value) {
      if (value instanceof Address) {
        realEditor = new AddressEditor();
        panel.setWidget((AddressEditor)realEditor);
      }
      else if (value instanceof Email) {
         realEditor = new EmailEditor();
         panel.setWidget((EmailEditor)realEditor);
      }
      else if (value instanceof PhoneNumber) {
         realEditor = new PhoneNumberEditor();
         panel.setWidget((PhoneNumberEditor)realEditor);
      }
      else {
         realEditor = null;
      }
   }
 }

Main Editor class:

public class AddEmployeeEditor extends Composite implements Editor<Employee> {


@UiField
ContactEditor contacts;

interface Driver extends SimpleBeanEditorDriver<Employee, AddEmployeeEditor> {
}  
public AddEmployeeEditor(final Binder binder) {
driver = GWT.create(Driver.class);
    driver.initialize(this);

    List<Contact> list = new ArrayList<Contact>();
    list.add(new Address());
    list.add(new Email());
    list.add(new PhoneNumber());
    list.add(new PhoneNumber());
    Employee employee = new Employee();
    employee.setContacts(list);
    driver.edit(employee);
   }
}

Can anyone tell me if this will work, am I going in the right direction or ?

Thanks in advance, Mac

I have updated the code above to now contain the ContactEditorWrapper class that Thomas advised.


Solution

  • That code has good chances to break: there's no guarantee that an editor returned by the EditorSource won't be used to edit another value in the list.

    You should create a wrapper editor implementing ValueAwareEditor and with the actual editor as a child editor with @Path(""); and create the appropriate ContactBaseEditor in the setValue method.

    class ContactEditor extends Composite implements ValueAwareEditor<Contact> {
    
       private SimplePanel panel = new SimplePanel();
    
       @Path("") ContactBaseEditor realEditor;
    
       public ContactEditor() {
          initWidget(panel);
       }
    
       @Override
       public void setValue(Contact value) {
          if (contact instanceof ContactAddress) {
             realEditor = new AddressEditor();
          }
          else if (contact instanceof ContactEmail) {
             realEditor = new EmailEditor();
          }
          else if (contact instanceof ContactPhoneNumber) {
             realEditor = new PhoneNumberEditor();
          }
          else {
             realEditor = null;
          }
          panel.setWidget(realEditor);
       }
    

    Note however that only the field/sub-editors from ContactBaseEditor will be edited, whichever the actual implementation being used. If there are additional fields/sub-editors in some ContactBaseEditor subclass, you'd have to implement ValueAwareEditor and handle things by-hand in the setValue and flush methods.