Mercurial > dive4elements > river
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 : |