package org.activityinfo.ui.client.component.form.field.hierarchy; import com.google.common.base.Function; import com.google.common.base.Supplier; import com.google.common.collect.Sets; import com.google.gwt.cell.client.ValueUpdater; import com.google.gwt.event.logical.shared.SelectionEvent; import com.google.gwt.event.logical.shared.SelectionHandler; import com.google.gwt.event.shared.HandlerRegistration; import org.activityinfo.core.client.InstanceQuery; import org.activityinfo.core.client.ResourceLocator; import org.activityinfo.core.shared.Projection; import org.activityinfo.core.shared.application.ApplicationProperties; import org.activityinfo.core.shared.criteria.ClassCriteria; import org.activityinfo.core.shared.criteria.CriteriaIntersection; import org.activityinfo.core.shared.criteria.FieldCriteria; import org.activityinfo.model.resource.ResourceId; import org.activityinfo.model.type.ReferenceValue; import org.activityinfo.promise.Promise; import javax.annotation.Nullable; import java.util.*; /** * Models the selection of hierarchy */ class Presenter { private Map<ResourceId, LevelView> widgetMap = new HashMap<>(); private Map<ResourceId, Projection> selection = new HashMap<>(); private List<HandlerRegistration> registrations = new ArrayList<>(); private ResourceLocator locator; private Hierarchy tree; private ValueUpdater valueUpdater; Presenter(ResourceLocator locator, final Hierarchy tree, Map<ResourceId, ? extends LevelView> widgets, ValueUpdater valueUpdater) { this.locator = locator; this.tree = tree; this.valueUpdater = valueUpdater; this.widgetMap.putAll(widgets); for(final Map.Entry<ResourceId, LevelView> entry : widgetMap.entrySet()) { entry.getValue().addSelectionHandler(new SelectionHandler<Projection>() { @Override public void onSelection(SelectionEvent<Projection> event) { onUserSelection(tree.getLevel(entry.getKey()), event.getSelectedItem()); } }); } } public Promise<Void> setInitialSelection(Iterable<ResourceId> resourceIds) { return setInitialSelection(new ReferenceValue(resourceIds)); } public Promise<Void> setInitialSelection(ReferenceValue value) { final InitialSelection initialSelection = new InitialSelection(tree); return initialSelection.fetch(locator, value.getResourceIds()).then(new Function<Void, Void>() { @Nullable @Override public Void apply(@Nullable Void input) { selection.putAll(initialSelection.getSelection()); for(Level level : tree.getLevels()) { LevelView view = widgetMap.get(level.getClassId()); if(level.isRoot() || hasSelection(level.getParent())) { view.setEnabled(true); view.setChoices(choices(level)); } else { view.setEnabled(false); } if(hasSelection(level)) { view.setSelection(getSelection(level)); } } return null; } }); } private void onUserSelection(Level level, Projection selectedItem) { if(selectedItem == null) { this.selection.remove(level.getClassId()); } else { this.selection.put(level.getClassId(), selectedItem); } clearChildren(level); valueUpdater.update(getValue()); } private ReferenceValue getValue() { // We want to store the values in a normalized fashion - // store only the leaf nodes, their parents are redundant Set<ResourceId> instanceIds = Sets.newHashSet(); Set<ResourceId> parentIds = Sets.newHashSet(); for(Projection projection : selection.values()) { instanceIds.add(projection.getRootInstanceId()); Set<ResourceId> parentId = projection.getReferenceValue(ApplicationProperties.PARENT_PROPERTY); if(!parentId.isEmpty()) { parentIds.add(parentId.iterator().next()); } } return new ReferenceValue(instanceIds); } private void clearChildren(Level parent) { Projection parentSelection = selection.get(parent.getClassId()); for(Level child : parent.getChildren()) { selection.remove(child.getClassId()); clearViewSelection(parentSelection, child); clearChildren(child); } } private void clearViewSelection(Projection parentSelection, Level child) { LevelView view = widgetMap.get(child.getClassId()); view.clearSelection(); if(parentSelection != null) { view.setChoices(choices(child)); view.setEnabled(true); } else { view.setEnabled(false); } } public boolean hasSelection(Level level) { return selection.containsKey(level.getClassId()); } public String getSelectionLabel(ResourceId classId) { assert selection.containsKey(classId) : "No selection"; return selection.get(classId).getStringValue(ApplicationProperties.LABEL_PROPERTY); } public Projection getSelection(Level level) { assert selection.containsKey(level.getClassId()); return selection.get(level.getClassId()); } private Supplier<Promise<List<Projection>>> choices(Level level) { final InstanceQuery.Builder query = InstanceQuery .select(ApplicationProperties.LABEL_PROPERTY, ApplicationProperties.PARENT_PROPERTY, level.getParentFieldId()); if(level.isRoot()) { query.where(new ClassCriteria(level.getClassId())); } else { Projection selectedParent = getSelection(level.getParent()); query.where(new CriteriaIntersection( new ClassCriteria(level.getClassId()), new FieldCriteria(level.getParentFieldId(), new ReferenceValue(selectedParent.getRootInstanceId())))); } return new Supplier<Promise<List<Projection>>>() { @Override public Promise<List<Projection>> get() { return locator.query(query.build()); } }; } }