comparison gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacageTwinPanel.java @ 5838:5aa05a7a34b7

Rename modules to more fitting names.
author Sascha L. Teichmann <teichmann@intevation.de>
date Thu, 25 Apr 2013 15:23:37 +0200
parents flys-client/src/main/java/org/dive4elements/river/client/client/ui/DatacageTwinPanel.java@821a02bbfb4e
children 172338b1407f
comparison
equal deleted inserted replaced
5837:d9901a08d0a6 5838:5aa05a7a34b7
1 package org.dive4elements.river.client.client.ui;
2
3 import com.google.gwt.core.client.GWT;
4 import com.google.gwt.user.client.rpc.AsyncCallback;
5
6 import com.smartgwt.client.data.Record;
7 import com.smartgwt.client.types.ListGridFieldType;
8 import com.smartgwt.client.widgets.Canvas;
9 import com.smartgwt.client.widgets.events.ClickEvent;
10 import com.smartgwt.client.widgets.grid.ListGrid;
11 import com.smartgwt.client.widgets.grid.ListGridField;
12 import com.smartgwt.client.widgets.grid.ListGridRecord;
13 import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
14 import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
15 import com.smartgwt.client.widgets.layout.HLayout;
16 import com.smartgwt.client.widgets.layout.VLayout;
17
18 import org.dive4elements.river.client.client.Config;
19 import org.dive4elements.river.client.client.FLYSConstants;
20 import org.dive4elements.river.client.client.event.StepForwardEvent;
21 import org.dive4elements.river.client.client.services.LoadArtifactServiceAsync;
22 import org.dive4elements.river.client.client.services.RemoveArtifactServiceAsync;
23 import org.dive4elements.river.client.shared.model.Artifact;
24 import org.dive4elements.river.client.shared.model.Collection;
25 import org.dive4elements.river.client.shared.model.Data;
26 import org.dive4elements.river.client.shared.model.DataItem;
27 import org.dive4elements.river.client.shared.model.DataList;
28 import org.dive4elements.river.client.shared.model.DefaultData;
29 import org.dive4elements.river.client.shared.model.DefaultDataItem;
30 import org.dive4elements.river.client.shared.model.Recommendation;
31 import org.dive4elements.river.client.shared.model.Recommendation.Facet;
32 import org.dive4elements.river.client.shared.model.Recommendation.Filter;
33 import org.dive4elements.river.client.shared.model.User;
34
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39
40 // TODO Probably better to branch off AbstractUIProvider.
41 // TODO Merge with other datacage-widget impls.
42 /**
43 * Panel containing a Grid and a "next" button. The Grid is fed by a
44 * DatacagePairWidget which is put in the input-helper area.
45 */
46 public class DatacageTwinPanel
47 extends TextProvider {
48
49 private static final long serialVersionUID = 8906629596491827857L;
50
51 protected static FLYSConstants MSG = GWT.create(FLYSConstants.class);
52
53 protected String dataName;
54
55 protected User user;
56
57 /** ListGrid that displays user-selected pairs to build differences with. */
58 protected ListGrid differencesList;
59
60 /**
61 * List to track previously selected but now removed pairs. (Needed to
62 * be able to identify artifacts that can be removed from the collection.
63 */
64 protected List<RecommendationPairRecord> removedPairs =
65 new ArrayList<RecommendationPairRecord>();
66
67 /** Service handle to clone and add artifacts to collection. */
68 LoadArtifactServiceAsync loadArtifactService = GWT.create(
69 org.dive4elements.river.client.client.services.LoadArtifactService.class);
70
71 /** Service to remove artifacts from collection. */
72 RemoveArtifactServiceAsync removeArtifactService = GWT.create(
73 org.dive4elements.river.client.client.services.RemoveArtifactService.class);
74
75
76 public DatacageTwinPanel(User user) {
77 super();
78 this.user = user;
79 }
80
81
82 /**
83 * Remove first occurrence of "[" and "]" (if both do occur).
84 * @param value String to be stripped of [] (might be null).
85 * @return input string but with [ and ] removed, or input string if no
86 * brackets were found.
87 * @see StringUtil.unbracket
88 */
89 public static final String unbracket(String value) {
90 // null- guard.
91 if (value == null) return value;
92
93 int start = value.indexOf("[");
94 int end = value.indexOf("]");
95
96 if (start < 0 || end < 0) {
97 return value;
98 }
99
100 value = value.substring(start + 1, end);
101
102 return value;
103 }
104
105
106 /**
107 * Create a recommendation from a string representation of it.
108 * @TODO describe format of input string
109 * @param from string in format as shown above.
110 * @return recommendation from input string.
111 */
112 public Recommendation createRecommendationFromString(String from) {
113 // TODO Construct "real" filter.
114 String[] parts = unbracket(from).split(";");
115 Recommendation.Filter filter = new Recommendation.Filter();
116 Recommendation.Facet facet = new Recommendation.Facet(
117 parts[1],
118 parts[2]);
119
120 List<Recommendation.Facet> facets = new ArrayList<Recommendation.Facet>
121 ();
122 facets.add(facet);
123 filter.add("longitudinal_section", facets);
124 Recommendation r = new Recommendation("waterlevel", parts[0],
125 this.artifact.getUuid(), filter);
126 r.setDisplayName(parts[3]);
127 return r;
128 }
129
130
131 /**
132 * Add RecomendationPairRecords from input String to the ListGrid.
133 */
134 public void populateGridFromString(String from){
135 // Split this string.
136 // Create according recommendations and display strings.
137 String[] recs = from.split("#");
138 if (recs.length % 2 != 0) return;
139 for (int i = 0; i < recs.length; i+=2) {
140 Recommendation minuend =
141 createRecommendationFromString(recs[i+0]);
142 Recommendation subtrahend =
143 createRecommendationFromString(recs[i+1]);
144
145 RecommendationPairRecord pr = new RecommendationPairRecord(
146 minuend, subtrahend);
147 // This Recommendation Pair comes from the data string and was thus
148 // already cloned.
149 pr.setIsAlreadyLoaded(true);
150 this.differencesList.addData(pr);
151 }
152 }
153
154
155 /**
156 * Creates the graphical representation and interaction widgets for the data.
157 * @param dataList the data.
158 * @return graphical representation and interaction widgets for data.
159 */
160 @Override
161 public Canvas create(DataList dataList) {
162 GWT.log("createData()");
163
164 Canvas widget = createWidget();
165 Canvas submit = getNextButton();
166
167 VLayout layout = new VLayout();
168 HLayout helperLayout = new HLayout();
169 helperLayout.addMember(new DatacagePairWidget(this.artifact,
170 user, "waterlevels", differencesList));
171
172 layout.addMember(widget);
173 layout.addMember(submit);
174 layout.setMembersMargin(10);
175 this.helperContainer.addMember(helperLayout);
176
177 // Find old data, if any, handle "diffids".
178 Data data = dataList.get(0);
179 this.dataName = data.getLabel();
180 for (int i = 0; i < dataList.size(); i++) {
181 if (dataList.get(i) != null && dataList.get(i).getItems() != null) {
182 if (dataList.get(i).getItems() != null) {
183 populateGridFromString(
184 dataList.get(i).getItems()[0].getStringValue());
185 }
186 }
187 }
188
189 return layout;
190 }
191
192
193 /**
194 * Validates the selection.
195 * @return List of internationalized errror messages (if any).
196 */
197 @Override
198 public List<String> validate() {
199 List<String> errors = new ArrayList<String>();
200 if (differencesList.getRecords().length == 0) {
201 errors.add(MSG.error_no_waterlevel_pair_selected());
202 }
203 // Check whether minuend and subtrahend are equal.
204 for (ListGridRecord record: differencesList.getRecords()) {
205 RecommendationPairRecord r = (RecommendationPairRecord) record;
206 if (r.getFirst().equals(r.getSecond())) {
207 errors.add(MSG.error_same_waterlevels_in_pair());
208 }
209 }
210
211 return errors;
212 }
213
214
215 /**
216 * Creates layout with grid that displays selection inside.
217 */
218 public Canvas createWidget() {
219 VLayout layout = new VLayout();
220 differencesList = new ListGrid();
221
222 differencesList.setCanEdit(false);
223 differencesList.setCanSort(false);
224 differencesList.setShowHeaderContextMenu(false);
225 differencesList.setHeight(150);
226 differencesList.setShowAllRecords(true);
227
228 ListGridField nameField = new ListGridField("first", "Minuend");
229 ListGridField capitalField = new ListGridField("second", "Subtrahend");
230 // Track removed rows, therefore more or less reimplement
231 // setCanRecomeRecords.
232 final ListGridField removeField =
233 new ListGridField("_removeRecord", "Remove Record"){{
234 setType(ListGridFieldType.ICON);
235 setIcon(GWT.getHostPageBaseURL() + MSG.removeFeature());
236 setCanEdit(false);
237 setCanFilter(false);
238 setCanSort(false);
239 setCanGroupBy(false);
240 setCanFreeze(false);
241 setWidth(25);
242 }};
243
244 differencesList.setFields(new ListGridField[] {nameField,
245 capitalField, removeField});
246
247 differencesList.addRecordClickHandler(new RecordClickHandler() {
248 @Override
249 public void onRecordClick(final RecordClickEvent event) {
250 // Just handle remove-clicks
251 if(!event.getField().getName().equals(removeField.getName())) {
252 return;
253 }
254 trackRemoved(event.getRecord());
255 event.getViewer().removeData(event.getRecord());
256 }
257 });
258 layout.addMember(differencesList);
259
260 return layout;
261 }
262
263
264 /**
265 * Add record to list of removed records.
266 */
267 public void trackRemoved(Record r) {
268 RecommendationPairRecord pr = (RecommendationPairRecord) r;
269 this.removedPairs.add(pr);
270 }
271
272
273 /**
274 * Validates data, does nothing if invalid, otherwise clones new selected
275 * waterlevels and add them to collection, forward the artifact.
276 */
277 @Override
278 public void onClick(ClickEvent e) {
279 GWT.log("DatacageTwinPanel.onClick");
280
281 List<String> errors = validate();
282 if (errors != null && !errors.isEmpty()) {
283 showErrors(errors);
284 return;
285 }
286
287 Config config = Config.getInstance();
288 String locale = config.getLocale();
289
290 ListGridRecord[] records = differencesList.getRecords();
291
292 List<Recommendation> ar = new ArrayList<Recommendation>();
293 List<Recommendation> all = new ArrayList<Recommendation>();
294
295 for (ListGridRecord record : records) {
296 RecommendationPairRecord r =
297 (RecommendationPairRecord) record;
298 // Do not add "old" recommendations.
299 if (!r.isAlreadyLoaded()) {
300 // Check whether one of those is a dike or similar.
301 // TODO differentiate and merge: new clones, new, old.
302 Recommendation firstR = r.getFirst();
303 if(firstR.getIDs() != null) {
304 GWT.log("First IDs: " + firstR.getIDs() + " factory: "
305 + firstR.getFactory());
306 }
307 if(firstR.getIDs() != null) {
308 // These do not get cloned but loaded ("spawned").
309 firstR.setFactory("staticwkms");
310 }
311 else {
312 firstR.setFactory("waterlevel");
313 }
314 Recommendation secondR = r.getSecond();
315 if(secondR.getIDs() != null) {
316 GWT.log("Second IDs: " + secondR.getIDs() + " factory: "
317 + secondR.getFactory());
318 }
319 if (secondR.getIDs() != null) {
320 // These do not get cloned but loaded ("spawned").
321 secondR.setFactory("staticwkms");
322 }
323 else {
324 secondR.setFactory("waterlevel");
325 }
326
327 ar.add(firstR);
328 ar.add(secondR);
329 }
330 else {
331 all.add(r.getFirst());
332 all.add(r.getSecond());
333 }
334 }
335
336 final Recommendation[] toClone = ar.toArray(new Recommendation[ar.size()]);
337 final Recommendation[] toUse = all.toArray(new Recommendation[all.size()]);
338
339 // Find out whether "old" artifacts have to be removed.
340 List<String> artifactIdsToRemove = new ArrayList<String>();
341 for (RecommendationPairRecord rp: this.removedPairs) {
342 Recommendation first = rp.getFirst();
343 Recommendation second = rp.getSecond();
344
345 for (Recommendation recommendation: toUse) {
346 if (first != null && first.getIDs().equals(recommendation.getIDs())) {
347 first = null;
348 }
349 if (second != null && second.getIDs().equals(recommendation.getIDs())) {
350 second = null;
351 }
352
353 if (first == null && second == null) {
354 break;
355 }
356 }
357 if (first != null) {
358 artifactIdsToRemove.add(first.getIDs());
359 }
360 if (second != null) {
361 artifactIdsToRemove.add(second.getIDs());
362 }
363 }
364
365 // Remove old artifacts, if any. Do this asychronously without much
366 // feedback.
367 for(final String uuid: artifactIdsToRemove) {
368 removeArtifactService.remove(this.collection,
369 uuid,
370 locale,
371 new AsyncCallback<Collection>() {
372 @Override
373 public void onFailure(Throwable caught) {
374 GWT.log("RemoveArtifact (" + uuid + ") failed.");
375 }
376 @Override
377 public void onSuccess(Collection collection) {
378 GWT.log("RemoveArtifact succeeded");
379 }
380 });
381 }
382
383 // Clone new ones (and spawn statics), go forward.
384 loadArtifactService.loadMany(
385 this.collection,
386 toClone,
387 //"staticwkms" and "waterlevel"
388 null,
389 locale,
390 new AsyncCallback<Artifact[]>() {
391 @Override
392 public void onFailure(Throwable caught) {
393 GWT.log("Failure of cloning with factories!");
394 }
395 @Override
396 public void onSuccess(Artifact[] artifacts) {
397 GWT.log("Successfully cloned " + toClone.length +
398 " with factories.");
399
400 fireStepForwardEvent(new StepForwardEvent(
401 getData(toClone, artifacts, toUse)));
402 }
403 });
404 }
405
406
407 /**
408 * Create Data and DataItem from selection (a long string with identifiers
409 * to construct diff-pairs).
410 *
411 * @param newRecommendations "new" recommendations (did not survive a
412 * backjump).
413 * @param newArtifacts artifacts cloned from newRecommendations.
414 * @param oldRecommendations old recommendations that survived a backjump.
415 *
416 * @return dataitem with a long string with identifiers to construct
417 * diff-pairs.
418 */
419 protected Data[] getData(
420 Recommendation[] newRecommendations,
421 Artifact[] newArtifacts,
422 Recommendation[] oldRecommendations)
423 {
424 // Construct string with info about selections.
425 String dataItemString = "";
426 for (int i = 0; i < newRecommendations.length; i++) {
427 Recommendation r = newRecommendations[i];
428 Artifact newArtifact = newArtifacts[i];
429 String uuid = newArtifact.getUuid();
430 r.setMasterArtifact(uuid);
431 if (i>0) dataItemString += "#";
432
433 dataItemString += createDataString(uuid, r);
434 }
435
436 for (int i = 0; i < oldRecommendations.length; i++) {
437 Recommendation r = oldRecommendations[i];
438 String uuid = r.getIDs();
439 if (dataItemString.length() > 0) dataItemString += "#";
440
441 dataItemString += createDataString(uuid, r);
442 }
443
444 // TODO some hassle could be resolved by using multiple DataItems
445 // (e.g. one per pair).
446 DataItem item = new DefaultDataItem(dataName, dataName, dataItemString);
447 return new Data[] { new DefaultData(
448 dataName, null, null, new DataItem[] {item}) };
449 }
450
451
452 /**
453 * Creates part of the String that encodes minuend or subtrahend.
454 * @param artifact Artifacts UUID.
455 * @param recommendation Recommendation to wrap in string.
456 */
457 protected String createDataString(
458 String artifact,
459 Recommendation recommendation)
460 {
461 Filter filter = recommendation.getFilter();
462 Facet f = null;
463
464 if(filter != null) {
465 Map<String, List<Facet>> outs = filter.getOuts();
466 Set<Map.Entry<String, List<Facet>>> entries = outs.entrySet();
467
468 for (Map.Entry<String, List<Facet>> entry: entries) {
469 List<Facet> fs = entry.getValue();
470
471 f = fs.get(0);
472 if (f != null) {
473 break;
474 }
475 }
476
477 return "[" + artifact + ";"
478 + f.getName()
479 + ";"
480 + f.getIndex()
481 + ";"
482 + recommendation.getDisplayName() + "]";
483 }
484 else {
485 return "["
486 + artifact
487 + ";staticwkms;0;"
488 + recommendation.getDisplayName() + "]";
489 }
490 }
491 }
492 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org