I'm trying to find some examples of table using lazy loading to load info, namely rows. I had a good look but I don't seem to be able to find any good example anywhere (I don't want to use any add on like Viritin), I just want to do it from scratch. The documentation on the vaadin website doesn't really help Table so I was just wondering if anybody is aware of any good tutorial that explains what needs to be done. Perhaps an example might be better then. So here is a simple table displaying integers up to 5000. I will try to implement lazy loading here which is a very simple application and then hopefully I will be able to integrate the functionality easy in my own application. Here is the code.
My UI class (MyUI.java):
public class MyUI extends UI {
#Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
numberTable theTable = new numberTable();
Button button = new Button("Click Me");
button.addClickListener(new Button.ClickListener()
{
#Override
public void buttonClick(ClickEvent event)
{
System.out.println("test!");
}
});
layout.addComponents(button, theTable);
layout.setMargin(true);
layout.setSpacing(true);
setContent(layout);
}
And the table class (numberTable.java):
package my.vaadin.project.tableTest;
import com.vaadin.ui.Table;
public class numberTable extends Table
{
public numberTable(){
/*addContainerProperty("Name", String.class, null);
addContainerProperty("Mag", Float.class, null);
addItem(new Object[]{"Canopus", -0.72f}, 1);
addItem(new Object[]{"Arcturus", -0.04f}, 2);
addItem(new Object[]{"Alpha Centauri", -0.01f}, 3);*/
addContainerProperty("Number", Integer.class, null);
for(int i=1; i<=5000; i++){
Integer itemID = new Integer(i);
addItem(new Object[]{i},itemID);
}
setCaption("Rendering table");
addStyleName("testTable");
setPageLength(size());
System.out.println("table created");
}
}
I've read that to implement the lazy loading functionality I have to have a container that supports it, other than the table, that's my understanding.
According to the documentation, the IndexedContainer matches your needs. Or, if you want to implement a container yourself that supports lazy loading with Table then implement the Container.Indexed interface. You could browse the IndexedContainer source code for an example.
I have made a basic example for implementing the Container.Indexed interface:
public class MyContainer implements Container.Indexed {
public Object nextItemId(Object itemId) { return ((Integer) itemId) + 1; }
public Object prevItemId(Object itemId) { return ((Integer) itemId) - 1; }
public Object firstItemId() { return 0; }
public Object lastItemId() { return 5000; }
public boolean isFirstId(Object itemId) { return Integer.valueOf(0).equals(itemId); }
public boolean isLastId(Object itemId) { return Integer.valueOf(5000).equals(itemId); }
public Item getItem(Object itemId) {
PropertysetItem item = new PropertysetItem();
item.addItemProperty("index", new ObjectProperty<Integer>((Integer) itemId));
return item;
}
public Collection<?> getContainerPropertyIds() { return Arrays.asList("index"); }
public Collection<?> getItemIds() { return Arrays.asList(IntStream.range(0, 5001).boxed().toArray(Integer[]::new)); }
public Property getContainerProperty(Object itemId, Object propertyId) { return new ObjectProperty<Integer>((Integer) itemId); }
public Class<?> getType(Object propertyId) { return Integer.class; }
public int size() { return 5001; }
public boolean containsId(Object itemId) {
Integer item = (Integer) itemId;
return item >= 0 && item <= 5000;
}
public int indexOfId(Object itemId) { return (Integer) itemId; }
public Object getIdByIndex(int index) { return index; }
public List<?> getItemIds(int startIndex, int numberOfItems) { return Arrays.asList(IntStream.range(0, 5001).boxed().toArray(Integer[]::new)).subList(startIndex, startIndex + numberOfItems); }
public Item addItem(Object itemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); }
public Object addItem() throws UnsupportedOperationException { throw new UnsupportedOperationException(); }
public boolean removeItem(Object itemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); }
public boolean addContainerProperty(Object propertyId, Class<?> type, Object defaultValue) throws UnsupportedOperationException { throw new UnsupportedOperationException(); }
public boolean removeContainerProperty(Object propertyId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); }
public boolean removeAllItems() throws UnsupportedOperationException { throw new UnsupportedOperationException(); }
public Object addItemAfter(Object previousItemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); }
public Item addItemAfter(Object previousItemId, Object newItemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); }
public Object addItemAt(int index) throws UnsupportedOperationException { throw new UnsupportedOperationException(); }
public Item addItemAt(int index, Object newItemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); }
}
It is read-only. Items have only one property "indexed" that is the index of the item, between 0 and 5000 inclusive. As you can see, it is a lot of work, so you should make use of a built-in container if possible.
Related
I have a code which goes like this
List insert;
List update;
List delete
for(SomeObject someObj : someObjects){
if(isNew){
insert.add()
}
else if(isUpdate){
update.add();
}
if(isDelete){
delete.add()
}
}
//call update insert delete functions
The problem is this code is untestable because the update insert delete are all void methods.
My question is , should I consider iterating over the loop three times and then get the lists to test if the logic to filter each type of results is working? The cost is not that much since I am expecting <100 elements in the list.
You can check
(sum of lists sizes after) == (sum of lists sizes before) + someObjects.size();
The key here is to be able to control the dependencies and access the state that this method acts upon.
Here is an example that illustrates that:
interface SomeDao {
void add(SomeObject object);
void update(SomeObject object);
void delete(SomeObject object);
}
class SomeDaoStub implements SomeDao {
#Override public void add(SomeObject object) {}
#Override public void update(SomeObject object) {}
#Override public void delete(SomeObject object) {}
}
class SomeObject {
private final boolean isNew;
private final boolean isUpdated;
private final boolean isDeleted;
SomeObject(boolean isNew, boolean isUpdated, boolean isDeleted) {
this.isNew = isNew;
this.isUpdated = isUpdated;
this.isDeleted = isDeleted;
}
public boolean isNew() {
return isNew;
}
public boolean isUpdated() {
return isUpdated;
}
public boolean isDeleted() {
return isDeleted;
}
}
public void doSomethingComplicatedWithListsInAForLoop(Iterable<SomeObject> someObjects, SomeDao dao) {
for (SomeObject someObject : someObjects) {
if (someObject.isNew()) {
dao.add(someObject);
} else if (someObject.isUpdated()) {
dao.update(someObject);
} else if (someObject.isDeleted()) {
dao.delete(someObject);
}
}
}
#Test
public void itDeletesObjectsMarkedToBeDeleted() {
final List<SomeObject> actualDeletedObjects = new ArrayList<>();
List<SomeObject> expectedDeletedObjects = Arrays.asList(
new SomeObject(false, false, true),
new SomeObject(false, false, true),
new SomeObject(false, false, true)
);
SomeDao theDao = new SomeDaoStub() {
#Override
public void delete(SomeObject object) {
actualDeletedObjects.add(object);
}
};
doSomethingComplicatedWithListsInAForLoop(expectedDeletedObjects, theDao);
assertEquals(expectedDeletedObjects, actualDeletedObjects);
}
The only reason that I can figure out what doSomethingComplicatedWithListsInAForLoop manipulated is because I can control its dependencies, namely, in this example, SomeDao.
It is likely that you are finding difficulty testing your method because it makes calls to state which you cannot inject.
I have a class structure like :
public interface DBReader {
public Map<String, String> read(String primaryKey, String valueOfPrimaryKey,
boolean scanIndexForward, boolean consistentRead, int maxPageSize);
public int getA(String ___);
public int getB(String ___);
public int getC(String ___);
}
public class DynamoDBReader implements DBReader {
private DynamoDB dynamoDB;
private String tableName;
private Table table;
private int throughput;
private DynamoDBReader(Builder builder) {
this.throughput = builder.throughput;
this.tableName = builder.tableName;
this.dynamoDB = builder.dynamoDB;
this.table = dynamoDB.getTable(builder.tableName);
if (table == null) {
throw new InvalidParameterException(String.format("Table %s doesn't exist.", tableName));
}
}
#Override
public int getA(String ____) {
read(_________);
}
return ________;
}
#Override
public int getB(String ____) {
read(_________);
}
return ________;
}
#Override
public int getC(String ____) {
read(_________);
}
return ________;
}
#Override
public Map<String, String> read(String primaryKey, String valueOfPrimaryKey, boolean scanIndexForward,
boolean consistentRead, int maxPageSize) {
QuerySpec spec = new QuerySpec()
.withHashKey(primaryKey, valueOfPrimaryKey)
.withScanIndexForward(scanIndexForward)
.withConsistentRead(consistentRead)
.withMaxPageSize(maxPageSize);
ItemCollection<QueryOutcome> items = table.query(spec);
Iterator<Item> itemIterator = items.firstPage().iterator();
Map<String, String> itemValues = new HashMap<String, String>();
while (itemIterator.hasNext()) {
Item item = itemIterator.next();
}
return itemValues;
}
}
#VisibleForTesting
protected void setTable(Table table) {
this.table = table;
}
/**
* Returns a new builder.
*/
public static Builder builder() {
return new Builder();
}
public static class Builder {
private String tableName;
private int throughput;
private DynamoDB dynamoDB;
private Builder() { }
public Builder tableName(String tableName) {
this.tableName = tableName;
return this;
}
public Builder throughput(int throughput) {
this.throughput = throughput;
return this;
}
public Builder dynamoDB(DynamoDB dynamoDB) {
this.dynamoDB = dynamoDB;
return this;
}
public DynamoDBReader build() {
if (tableName == null) {
throw new InvalidParameterException("Table name can't be null.");
}
if (throughput <= 0) {
throw new InvalidParameterException("Throughput should be > 0.");
}
if (dynamoDB == null) {
throw new InvalidParameterException("dynamoDB can't be null.");
}
return new DynamoDBReader(this);
}
}
}
Problem : getA(), getB(), getC() are only valid for specific tableNames. For a table getA() is Valid but getB() and getC() wont make any sense.
How to couple method names with table name so that someone with a table name knows which function is valid.
Solution to create subclasses for different getters doesn't look a great idea to me.
Solution to create subclasses for different getters doesn't look a great idea to me.
Can you please elaborate why?
I hear that all the time, 'I don't like it...', 'This seems ugly...', 'It shouldn't do that'. Reasons for not liking a particular solution should be backed by objective reasons, not personal opinions. Most of the time our intuition as developers tells us that something is wrong when it is actually violating some software development principle. But sometimes it is just plain old personal feeling without any particular logical reason. When that happens I like to get to specifics.
Your solution violates a basic software principle called SRP.
Having table modules will be much better solution.
Using GWT 2.6.1, UiBinder, DataGrid.
Also using SingleSelectionModel to select a single row:
final SingleSelectionModel<User> selectionModel = new SingleSelectionModel<>(keyProvider);
Checkboxes column:
// checkboxes
Column<User, Boolean> checkBoxColumn = new Column<User, Boolean>(
new CheckboxCell(false, false)) {
#Override
public Boolean getValue(User user) {
return user.isChecked();
}
};
checkBoxColumn.setFieldUpdater(new FieldUpdater<User, Boolean>() {
#Override
public void update(int index, User user, Boolean value) {
user.setChecked(value);
}
});
So i store "checked" user state as a boolean field in the User entity class, without
using a SelectionModel at all.
Now I need to implement custom header checkbox to select/deselect all checkboxes in the column.
public class CheckboxHeader extends Header<Boolean> {
public CheckboxHeader(CheckboxCell cell) {
super(cell);
}
#Override
public Boolean getValue() {
return null;
}
}
Have no ideas how to implement properly this header class to add column in the DataGrid:
dataGrid.addColumn(checkBoxColumn, new CheckboxHeader(new CheckboxCell(false, false)));
Another trouble is to enable/disable all those checkboxes by checking other checkbox that
isn't in the DataGrid.
How can i retrieve all checkboxes from the column/selectionmodel/etc and enable/disable them one by one?
Appreciate any suggestions.
Mixing the data model (User entity) and the state of user interface (isSelected) is never a good idea.
This is how you can do it (replace T with your object, or create a column object that you can re-use):
Column<T, Boolean> checkColumn = new Column<T, Boolean>(new CheckboxCell()) {
#Override
public Boolean getValue(T object) {
return getSelectionModel().isSelected(object);
}
};
checkColumn.setFieldUpdater(new FieldUpdater<T, Boolean>() {
#Override
public void update(int index, T object, Boolean value) {
getSelectionModel().setSelected(object, value);
dataProvider.refresh();
}
});
myDataGrid.setSelectionModel(getSelectionModel(), DefaultSelectionEventManager.<T> createCheckboxManager(0));
Header<Boolean> selectAllHeader = new Header<Boolean>(new HeaderCheckbox()) {
#Override
public Boolean getValue() {
for (T item : getVisibleItems()) {
if (!getSelectionModel().isSelected(item)) {
return false;
}
}
return getVisibleItems().size() > 0;
}
};
selectAllHeader.setUpdater(new ValueUpdater<Boolean>() {
#Override
public void update(Boolean value) {
for (T object : getVisibleItems()) {
getSelectionModel().setSelected(object, value);
}
}
});
myDataGrid.addColumn(checkColumn, selectAllHeader);
I try to use DataBinding on SWT Widgets.
I´m wondering if there is a way to connect a Combo Box to an underlying String in the model.
So I have a String in the Model and a Combo on my View?
As the standard way is not working:
//View
DataBindingContext ctx = new DataBindingContext();
IObservableValue target1 = WidgetProperties.singleSelectionIndex().observe(combo);
IObservableValue model1 = BeanProperties.value(OutputVariable.class, "type").observe(outputVariable);
ctx.bindValue(target1, model1);
//Model
public void setType(String type) {
//TYPES is a constant with the possible Combo values
if (contains(TYPES, type)) {
String oldType = this.type;
this.type = type;
firePropertyChange("type", oldType, this.type);
}else {
throw new IllegalArgumentException();
}
}
I tried to use the fireIndexedPropertyChangeMethod which didn't worked either.
Is there a way to connect those two together? Maybe I have to use another WidgetProperties or BeanProperties method?
As a workaround I could maybe use a new Property in the model, which defines the combo selection index, connect this to the Combo and transfer changes of this index to the type Property and vice versa. But that seems not as a great solution to me.
Edit:
The Solution with a selectionIndex Property is working. But a cleaner method would still be nice as now a type Property change in the model has to reset the selectionIndex too and vice versa.
I have a clean solution now, which is to use a Converter.
//View
IObservableValue comboObservable = WidgetProperties.singleSelectionIndex().observe(combo);
IObservableValue viewTypeObservable = BeanProperties.value(DebugModel.class, "type").observe(debugModel);
IConverter viewTypeToIntConverter = createViewTypeToIntConverter();
UpdateValueStrategy toTargetStrategy = new UpdateValueStrategy();
toTargetStrategy.setConverter(viewTypeToIntConverter);
IConverter intToViewTypeConverter = createIntToViewTypeConverter();
UpdateValueStrategy toModelStrategy = new UpdateValueStrategy();
toModelStrategy.setConverter(intToViewTypeConverter);
DataBindingContext context = new DataBindingContext();
context.bindValue(comboObservable, viewTypeObservable, toModelStrategy, toTargetStrategy);
//Converter
private IConverter createIntToViewTypeConverter() {
return new IConverter() {
#Override
public Object convert(Object value) {
if(value instanceof Integer) {
for(ViewType type : ViewType.values()) {
if(type.toString().equals(ViewType.getStringAtIndex((int)value))) {
return type;
}
}
}
throw new IllegalArgumentException("We need an Integer to convert it but got an " + value.getClass());
}
#Override
public Object getFromType() {
return Integer.class;
}
#Override
public Object getToType() {
return ViewType.class;
}
};
}
private IConverter createViewTypeToIntConverter() {
return new IConverter() {
#Override
public Object convert(Object value) {
if(value instanceof ViewType) {
String[] viewTypes = ViewType.getStringValues();
for(int i=0;i<viewTypes.length;i++) {
if(viewTypes[i].equals(((ViewType)value).toString())) {
return i;
}
}
}
throw new IllegalArgumentException("We need a View Type to be converted but got a " + value.getClass());
}
#Override
public Object getFromType() {
return ViewType.class;
}
#Override
public Object getToType() {
return Integer.class;
}
};
}
//Model
public class DebugModel extends ModelObject {
private ViewType type;
public ViewType getType() {
return type;
}
public void setType(ViewType type) {
firePropertyChange("type", this.type, this.type = type);
}
}
//Just to complete the example, be sure the Model class extends a ModelObject class like this
public class ModelObject {
private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
this);
public void addPropertyChangeListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
public void addPropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(propertyName, listener);
}
protected void firePropertyChange(String propertyName, Object oldValue,
Object newValue) {
changeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
protected void fireIndexedPropertyChange(String propertyName, int index, Object oldValue, Object newValue) {
changeSupport.fireIndexedPropertyChange(propertyName, index, oldValue, newValue);
}
}
Of course you can outsource the Converters to custom classes, I used it this way just to show a quick solution here.
I have a simple OutlineView in the NetBeans editor area that shows two columns. The content of the cells of the second column shall be settable with a custom property editor via the PropertySupport. The custom property editor contains a JList that allows multiple selection of items.
The PropertySupport class looks like
public class CityProperty extends PropertySupport.ReadWrite<String> {
Customer c;
public CityProperty(Customer c, HashMap<String, Boolean> optionalCities) {
super("city", String.class, "City", "Name of City");
setValue("labelData", optionalCities);
this.c = c;
}
#Override
public String getValue() throws IllegalAccessException, InvocationTargetException {
return c.getCity();
}
#Override
public PropertyEditor getPropertyEditor() {
return new CityPropertyEditor(c);
}
#Override
public void setValue(String newValue) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
c.setCity(newValue);
}
}
The PropertyEditor looks like
public class CityPropertyEditor extends PropertyEditorSupport implements ExPropertyEditor {
Customer c;
PropertyEnv env;
public CityPropertyEditorPanel editor = null;
public CityPropertyEditor(Customer c) {
this.editor = new CityPropertyEditorPanel();
this.c = c;
}
#Override
public String getAsText() {
String s = (String) getValue();
if (s == null) {
return "No City Set";
}
return s;
}
#Override
public void setAsText(String s) {
setValue(s);
}
#Override
public void attachEnv(PropertyEnv env) {
this.env = env;
}
#Override
public Component getCustomEditor() {
HashMap<String, Boolean> cities = (HashMap<String, Boolean>) env.getFeatureDescriptor().getValue("labelData");
DefaultListModel model = new DefaultListModel();
/* selection in the gui */
int[] selectedIdxs = new int[cities.size()];
int idx = 0;
for (String str : cities.keySet()) {
model.addElement(str);
if (cities.get(str) == Boolean.FALSE) {
selectedIdxs[idx] = model.indexOf(str);
idx++;
}
}
if (selectedIdxs.length > 0){
editor.jList.setSelectedIndices(selectedIdxs);
}
editor.jList.setModel(model);
return editor;
}
#Override
public boolean supportsCustomEditor() {
return true;
}
#Override
public Object getValue() {
System.out.println("getValue(): " + editor.jList.getSelectedValuesList());
System.out.println("getValue(): " + editor.jtf.getText());
return super.getValue();
}
}
and the editor CityPropertyEditorPanel() itself is a simple JPanel with a JList and a JTextField.
My codes creates a nice custom editor with all the items listed, but it is not returning the new selected items from the list. My question is now, how do I get the selected items from the JList back to the CityProperty class? My try was to use
editor.jList.getSelectedValuesList());
in the getValue() method but the result is always empty. The same for the JTextField, where a new written value is also not transferred back.
What Am I doing wrong here?
I think I found a solution/workaround.
The CityPropertyEditor recognized the content of the "editor" object when I activated the PropertyEnv.STATE_NEEDS_VALIDATION feature. The code then in CityPropertyEditor should have to override the attacheEnv method and include the VetoableChangeListener
#Override
public void attachEnv(PropertyEnv env) {
this.env = env;
env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
env.addVetoableChangeListener(new VetoableChangeListener() {
#Override
public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
/* User has pushed OK */
for (Entry entry : editor.isoValNew.entrySet()){
isoVal.put((Double) entry.getKey(), (Boolean) entry.getValue());
}
}
});
}
while the Jlist in the CityPropertyEditorPanel() itself has a ListSelectionListener who updates the Map variable isoValNew
isoValueList.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
isoValNew.clear();
for (Object obj : isoValueList.getSelectedValues()) {
isoValNew.put((Double) obj, Boolean.TRUE);
}
}
});
I'm sure this is not a perfect solution, but it works fine in my case.
Hope this helps someone.