package org.activityinfo.core.client.form.tree; import com.google.common.base.Function; import com.google.gwt.user.client.rpc.AsyncCallback; import org.activityinfo.core.client.ResourceLocator; import org.activityinfo.model.resource.ResourceId; import org.activityinfo.core.shared.criteria.FormClassSet; import org.activityinfo.model.form.FormClass; import org.activityinfo.model.form.FormField; import org.activityinfo.model.formTree.FormTree; import org.activityinfo.promise.Promise; import java.util.Collections; import java.util.logging.Logger; /** * Builds a {@link FormTree} */ public class AsyncFormTreeBuilder implements Function<ResourceId, Promise<FormTree>> { private static final Logger LOGGER = Logger.getLogger(AsyncFormTreeBuilder.class.getName()); private final ResourceLocator locator; public AsyncFormTreeBuilder(ResourceLocator locator) { this.locator = locator; } @Override public Promise<FormTree> apply(ResourceId formClassId) { return execute(Collections.singleton(formClassId)); } public Promise<FormTree> execute(Iterable<ResourceId> formClasses) { Promise<FormTree> result = new Promise<>(); new Resolver(formClasses, result); return result; } public class Resolver { private AsyncCallback<? super FormTree> callback; private FormTree tree; private int outstandingRequests = 0; public Resolver(final Iterable<ResourceId> classIds, final AsyncCallback<FormTree> callback) { this.callback = callback; this.tree = new FormTree(); for(ResourceId formClass : classIds) { requestFormClassForNode(null, formClass); } } private void requestFormClassForNode(final FormTree.Node node, final ResourceId formClassId) { LOGGER.fine("Requesting form class for " + node); outstandingRequests++; locator.getFormClass(formClassId).then(new AsyncCallback<FormClass>() { @Override public void onFailure(Throwable caught) { Resolver.this.callback.onFailure(caught); } @Override public void onSuccess(FormClass formClass) { addChildrenToNode(node, formClass); outstandingRequests--; if(outstandingRequests == 0) { callback.onSuccess(tree); } } }); } /** * Now that we have the actual FormClass model that corresponds to this node's * formClassId, add it's children. * */ private void addChildrenToNode(FormTree.Node node, FormClass formClass) { for(FormField field : formClass.getFields()) { FormTree.Node childNode; if(node == null) { childNode = tree.addRootField(formClass, field); } else { childNode = node.addChild(formClass, field); } if(childNode.isReference()) { queueNextRequests(childNode); } } } private void queueNextRequests(FormTree.Node child) { FormClassSet classSet = FormClassSet.of(child.getRange()); assert classSet.isClosed() : "trees for open class ranges not yet implemented"; for(ResourceId classId : classSet.getElements()) { requestFormClassForNode(child, classId); } } } }