teichmann@5861: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5861: * Software engineering by Intevation GmbH teichmann@5861: * teichmann@5993: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5861: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5993: * documentation coming with Dive4Elements River for details. teichmann@5861: */ teichmann@5861: teichmann@5835: package org.dive4elements.river.client.client.ui.minfo; raimund@3508: sascha@3535: import com.google.gwt.core.client.GWT; christian@4183: sascha@3535: import com.google.gwt.user.client.rpc.AsyncCallback; sascha@3535: sascha@3535: import com.smartgwt.client.data.Record; christian@4183: rrenkert@7971: import com.smartgwt.client.util.SC; rrenkert@7971: import com.smartgwt.client.widgets.Button; sascha@3535: import com.smartgwt.client.widgets.Canvas; christian@4183: sascha@3535: import com.smartgwt.client.widgets.events.ClickEvent; rrenkert@7971: import com.smartgwt.client.widgets.events.ClickHandler; sascha@3535: import com.smartgwt.client.widgets.grid.ListGridRecord; christian@4183: sascha@3535: import com.smartgwt.client.widgets.layout.HLayout; sascha@3535: import com.smartgwt.client.widgets.layout.VLayout; rrenkert@7971: import com.smartgwt.client.widgets.tree.TreeNode; sascha@3535: teichmann@5835: import org.dive4elements.river.client.client.Config; teichmann@5835: import org.dive4elements.river.client.client.FLYSConstants; christian@4183: teichmann@5835: import org.dive4elements.river.client.client.event.StepForwardEvent; christian@4183: teichmann@5835: import org.dive4elements.river.client.client.services.LoadArtifactService; teichmann@5835: import org.dive4elements.river.client.client.services.LoadArtifactServiceAsync; teichmann@5835: import org.dive4elements.river.client.client.services.RemoveArtifactService; teichmann@5835: import org.dive4elements.river.client.client.services.RemoveArtifactServiceAsync; christian@4183: teichmann@5835: import org.dive4elements.river.client.client.ui.DatacagePairWidget; teichmann@5835: import org.dive4elements.river.client.client.ui.DatacageTwinPanel; rrenkert@7971: import org.dive4elements.river.client.client.ui.DatacageWidget; teichmann@5835: import org.dive4elements.river.client.client.ui.RecommendationPairRecord; christian@4183: teichmann@5835: import org.dive4elements.river.client.shared.model.Artifact; teichmann@5835: import org.dive4elements.river.client.shared.model.Collection; teichmann@5835: import org.dive4elements.river.client.shared.model.Data; teichmann@5835: import org.dive4elements.river.client.shared.model.DataItem; teichmann@5835: import org.dive4elements.river.client.shared.model.DataList; rrenkert@7971: import org.dive4elements.river.client.shared.model.ToLoad; teichmann@5835: teichmann@5835: import org.dive4elements.river.client.shared.model.Recommendation.Facet; teichmann@5835: import org.dive4elements.river.client.shared.model.Recommendation.Filter; teichmann@5835: teichmann@5835: import org.dive4elements.river.client.shared.model.Recommendation; teichmann@5835: import org.dive4elements.river.client.shared.model.User; sascha@3535: raimund@3508: import java.util.ArrayList; raimund@3508: import java.util.List; raimund@3508: import java.util.Map; raimund@3508: import java.util.Set; raimund@3508: raimund@3508: // TODO Probably better to branch off AbstractUIProvider. raimund@3508: // TODO Merge with other datacage-widget impls. raimund@3508: /** raimund@3508: * Panel containing a Grid and a "next" button. The Grid is fed by a raimund@3508: * DatacagePairWidget which is put in the input-helper area. raimund@3508: */ raimund@3508: public class BedHeightsDatacagePanel raimund@3508: extends DatacageTwinPanel { raimund@3508: raimund@3508: protected static FLYSConstants MSG = GWT.create(FLYSConstants.class); raimund@3508: raimund@3508: /** raimund@3508: * List to track previously selected but now removed pairs. (Needed to raimund@3508: * be able to identify artifacts that can be removed from the collection. raimund@3508: */ raimund@3508: protected List removedPairs = raimund@3508: new ArrayList(); raimund@3508: raimund@3508: /** Service handle to clone and add artifacts to collection. */ raimund@3508: LoadArtifactServiceAsync loadArtifactService = GWT.create( teichmann@5835: org.dive4elements.river.client.client.services.LoadArtifactService.class); raimund@3508: raimund@3508: /** Service to remove artifacts from collection. */ raimund@3508: RemoveArtifactServiceAsync removeArtifactService = GWT.create( teichmann@5835: org.dive4elements.river.client.client.services.RemoveArtifactService.class); raimund@3508: rrenkert@7971: protected DatacageWidget datacage; raimund@3508: raimund@3508: public BedHeightsDatacagePanel(User user) { raimund@3508: super(user); raimund@3508: } raimund@3508: raimund@3508: raimund@3508: /** raimund@3508: * Create a recommendation from a string representation of it. raimund@3508: * @TODO describe format of input string raimund@3508: * @param from string in format as shown above. raimund@3508: * @return recommendation from input string. raimund@3508: */ raimund@3508: public Recommendation createRecommendationFromString(String from) { raimund@3508: // TODO Construct "real" filter. raimund@3508: String[] parts = unbracket(from).split(";"); raimund@3508: Recommendation.Filter filter = new Recommendation.Filter(); raimund@3508: Recommendation.Facet facet = new Recommendation.Facet( raimund@3508: parts[1], raimund@3508: parts[2]); raimund@3508: raimund@3508: List facets = new ArrayList raimund@3508: (); raimund@3508: facets.add(facet); raimund@3508: filter.add("longitudinal_section", facets); raimund@3508: Recommendation r = new Recommendation("bedheight", parts[0], raimund@3508: this.artifact.getUuid(), filter); raimund@3508: r.setDisplayName(parts[3]); raimund@3508: return r; raimund@3508: } raimund@3508: raimund@3508: raimund@3508: /** raimund@3508: * Creates the graphical representation and interaction widgets for the data. raimund@3508: * @param dataList the data. raimund@3508: * @return graphical representation and interaction widgets for data. raimund@3508: */ raimund@3508: @Override raimund@3508: public Canvas create(DataList dataList) { raimund@3508: GWT.log("createData()"); raimund@3508: rrenkert@7971: String filter = "minfo-heights-diff"; raimund@3508: Canvas widget = createWidget(); raimund@3508: Canvas submit = getNextButton(); rrenkert@7971: datacage = new DatacageWidget( rrenkert@7971: this.artifact, user, filter, "load-system:true", false); rrenkert@7971: rrenkert@7971: Button plusBtn = new Button(MSG.datacage_add_pair()); rrenkert@7971: plusBtn.setAutoFit(true); rrenkert@7971: plusBtn.addClickHandler(new ClickHandler() { rrenkert@7971: @Override rrenkert@7971: public void onClick(ClickEvent event) { rrenkert@7971: plusClicked(); rrenkert@7971: } rrenkert@7971: }); raimund@3508: raimund@3508: VLayout layout = new VLayout(); rrenkert@7971: VLayout helperLayout = new VLayout(); rrenkert@7971: helperLayout.addMember(datacage); rrenkert@7971: helperLayout.addMember(plusBtn); raimund@3508: raimund@3508: layout.addMember(widget); raimund@3508: layout.addMember(submit); raimund@3508: layout.setMembersMargin(10); raimund@3508: this.helperContainer.addMember(helperLayout); raimund@3508: raimund@3508: this.dataName = "diffids"; raimund@3508: raimund@3508: return layout; raimund@3508: } raimund@3508: raimund@3508: raimund@3508: /** raimund@3508: * Add record to list of removed records. raimund@3508: */ raimund@3508: public void trackRemoved(Record r) { raimund@3508: RecommendationPairRecord pr = (RecommendationPairRecord) r; raimund@3508: this.removedPairs.add(pr); raimund@3508: } raimund@3508: raimund@3508: raimund@3508: /** raimund@3508: * Validates data, does nothing if invalid, otherwise clones new selected raimund@3508: * waterlevels and add them to collection, forward the artifact. raimund@3508: */ raimund@3508: @Override raimund@3508: public void onClick(ClickEvent e) { raimund@3508: GWT.log("DatacageTwinPanel.onClick"); raimund@3508: raimund@3508: List errors = validate(); raimund@3508: if (errors != null && !errors.isEmpty()) { raimund@3508: showErrors(errors); raimund@3508: return; raimund@3508: } raimund@3508: raimund@3508: Config config = Config.getInstance(); raimund@3508: String locale = config.getLocale(); raimund@3508: raimund@3508: ListGridRecord[] records = differencesList.getRecords(); raimund@3508: raimund@3508: List ar = new ArrayList(); raimund@3508: List all = new ArrayList(); raimund@3508: raimund@3508: for (ListGridRecord record : records) { raimund@3508: RecommendationPairRecord r = raimund@3508: (RecommendationPairRecord) record; raimund@3508: // Do not add "old" recommendations. raimund@3508: if (!r.isAlreadyLoaded()) { raimund@3508: // Check whether one of those is a dike or similar. raimund@3508: // TODO differentiate and merge: new clones, new, old. raimund@3508: Recommendation firstR = r.getFirst(); raimund@3508: if(firstR.getIDs() != null) { raimund@3508: GWT.log("First IDs: " + firstR.getIDs() + " factory: " raimund@3508: + firstR.getFactory()); raimund@3508: } raimund@3508: firstR.setFactory("bedheight"); raimund@3508: Recommendation secondR = r.getSecond(); raimund@3508: if(secondR.getIDs() != null) { raimund@3508: GWT.log("Second IDs: " + secondR.getIDs() + " factory: " raimund@3508: + secondR.getFactory()); raimund@3508: } raimund@3508: secondR.setFactory("bedheight"); raimund@3508: raimund@3508: ar.add(firstR); raimund@3508: ar.add(secondR); raimund@3508: } raimund@3508: else { raimund@3508: all.add(r.getFirst()); raimund@3508: all.add(r.getSecond()); raimund@3508: } raimund@3508: } raimund@3508: raimund@3508: final Recommendation[] toClone = ar.toArray(new Recommendation[ar.size()]); raimund@3508: final Recommendation[] toUse = all.toArray(new Recommendation[all.size()]); raimund@3508: raimund@3508: // Find out whether "old" artifacts have to be removed. raimund@3508: List artifactIdsToRemove = new ArrayList(); raimund@3508: for (RecommendationPairRecord rp: this.removedPairs) { raimund@3508: Recommendation first = rp.getFirst(); raimund@3508: Recommendation second = rp.getSecond(); raimund@3508: raimund@3508: for (Recommendation recommendation: toUse) { raimund@3508: if (first != null && first.getIDs().equals(recommendation.getIDs())) { raimund@3508: first = null; raimund@3508: } raimund@3508: if (second != null && second.getIDs().equals(recommendation.getIDs())) { raimund@3508: second = null; raimund@3508: } raimund@3508: raimund@3508: if (first == null && second == null) { raimund@3508: break; raimund@3508: } raimund@3508: } raimund@3508: if (first != null) { raimund@3508: artifactIdsToRemove.add(first.getIDs()); raimund@3508: } raimund@3508: if (second != null) { raimund@3508: artifactIdsToRemove.add(second.getIDs()); raimund@3508: } raimund@3508: } raimund@3508: raimund@3508: // Remove old artifacts, if any. Do this asychronously without much raimund@3508: // feedback. raimund@3508: for(final String uuid: artifactIdsToRemove) { raimund@3508: removeArtifactService.remove(this.collection, raimund@3508: uuid, raimund@3508: locale, raimund@3508: new AsyncCallback() { raimund@3508: public void onFailure(Throwable caught) { raimund@3508: GWT.log("RemoveArtifact (" + uuid + ") failed."); raimund@3508: } raimund@3508: public void onSuccess(Collection collection) { raimund@3508: GWT.log("RemoveArtifact succeeded"); raimund@3508: } raimund@3508: }); raimund@3508: } raimund@3508: raimund@3508: // Clone new ones (and spawn statics), go forward. raimund@3508: loadArtifactService.loadMany( raimund@3508: this.collection, raimund@3508: toClone, raimund@3508: //"staticwkms" and "waterlevel" raimund@3508: null, raimund@3508: locale, raimund@3508: new AsyncCallback() { raimund@3508: public void onFailure(Throwable caught) { raimund@3508: GWT.log("Failure of cloning with factories!"); raimund@3508: } raimund@3508: public void onSuccess(Artifact[] artifacts) { raimund@3508: GWT.log("Successfully cloned " + toClone.length + raimund@3508: " with factories."); raimund@3508: raimund@3508: fireStepForwardEvent(new StepForwardEvent( raimund@3508: getData(toClone, artifacts, toUse))); raimund@3508: } raimund@3508: }); raimund@3508: } raimund@3508: raimund@3508: raimund@3508: /** raimund@3508: * Creates part of the String that encodes minuend or subtrahend. raimund@3508: * @param artifact Artifacts UUID. raimund@3508: * @param recommendation Recommendation to wrap in string. raimund@3508: */ raimund@3508: protected String createDataString( raimund@3508: String artifact, raimund@3508: Recommendation recommendation) raimund@3508: { raimund@3508: Filter filter = recommendation.getFilter(); raimund@3508: Facet f = null; raimund@3508: raimund@3508: if(filter != null) { raimund@3508: Map> outs = filter.getOuts(); raimund@3508: Set>> entries = outs.entrySet(); raimund@3508: raimund@3508: for (Map.Entry> entry: entries) { raimund@3508: List fs = entry.getValue(); raimund@3508: raimund@3508: f = fs.get(0); raimund@3508: if (f != null) { raimund@3508: break; raimund@3508: } raimund@3508: } raimund@3508: raimund@3508: return "[" + artifact + ";" raimund@3508: + f.getName() raimund@3508: + ";" raimund@3508: + f.getIndex() raimund@3508: + ";" raimund@3508: + recommendation.getDisplayName() + "]"; raimund@3508: } raimund@3508: else { raimund@3508: return "[" raimund@3508: + artifact raimund@3508: + ";bedheight;0;" raimund@3508: + recommendation.getDisplayName() + "]"; raimund@3508: } raimund@3508: } rrenkert@7971: rrenkert@7971: /** rrenkert@7971: * Callback for add-button. rrenkert@7971: * Fires to load for every selected element and handler. rrenkert@7971: */ rrenkert@7971: public void plusClicked() { rrenkert@7971: List selection = datacage.getPlainSelection(); rrenkert@7971: rrenkert@7971: if (selection == null || selection.isEmpty()) { rrenkert@7971: SC.say(MSG.warning()); rrenkert@7971: return; rrenkert@7971: } rrenkert@7971: rrenkert@7971: for (TreeNode node : selection) { rrenkert@7971: ToLoad toLoad1 = new ToLoad(); rrenkert@7971: ToLoad toLoad2 = new ToLoad(); rrenkert@7971: rrenkert@7971: String factory = node.getAttribute("factory"); rrenkert@7971: if (factory != null) { // we need at least a factory rrenkert@7971: String artifact = node.getAttribute("artifact-id"); rrenkert@7971: String out = node.getAttribute("out"); rrenkert@7971: String name = node.getAttribute("facet"); rrenkert@7971: String ids = node.getAttribute("ids"); rrenkert@7971: String info = node.getAttribute("info"); rrenkert@7971: String targetOut = node.getAttribute("target_out"); rrenkert@7971: rrenkert@7971: String[] splitIds = ids.split("#"); rrenkert@7971: String[] splitInfo = info.split("#"); rrenkert@7971: toLoad1.add(artifact, rrenkert@7971: factory, rrenkert@7971: out, rrenkert@7971: name, rrenkert@7971: splitIds[0], rrenkert@7971: splitInfo[0], rrenkert@7971: targetOut); rrenkert@7971: toLoad2.add(artifact, rrenkert@7971: factory, rrenkert@7971: out, rrenkert@7971: name, rrenkert@7971: splitIds[1], rrenkert@7971: splitInfo[1], rrenkert@7971: targetOut); rrenkert@7971: } rrenkert@7971: differencesList.addData(new RecommendationPairRecord( rrenkert@7971: toLoad1.toRecommendations().get(0), rrenkert@7971: toLoad2.toRecommendations().get(0))); rrenkert@7971: } rrenkert@7971: } raimund@3508: } raimund@3508: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :