11
11
import com .gooddata .sdk .model .executeafm .Execution ;
12
12
import com .gooddata .sdk .model .executeafm .afm .Afm ;
13
13
import com .gooddata .sdk .model .executeafm .afm .AttributeItem ;
14
+ import com .gooddata .sdk .model .executeafm .afm .NativeTotalItem ;
14
15
import com .gooddata .sdk .model .executeafm .afm .filter .CompatibilityFilter ;
15
16
import com .gooddata .sdk .model .executeafm .afm .filter .DateFilter ;
16
17
import com .gooddata .sdk .model .executeafm .afm .filter .ExtendedFilter ;
24
25
import com .gooddata .sdk .model .executeafm .resultspec .Dimension ;
25
26
import com .gooddata .sdk .model .executeafm .resultspec .ResultSpec ;
26
27
import com .gooddata .sdk .model .executeafm .resultspec .SortItem ;
28
+ import com .gooddata .sdk .model .executeafm .resultspec .TotalItem ;
29
+ import com .gooddata .sdk .model .md .report .Total ;
27
30
28
31
import java .util .ArrayList ;
32
+ import java .util .HashSet ;
29
33
import java .util .List ;
30
34
import java .util .function .Function ;
31
35
import java .util .stream .Collectors ;
@@ -43,9 +47,12 @@ public abstract class VisualizationConverter {
43
47
44
48
/**
45
49
* Generate Execution from Visualization object.
50
+ * <p>
51
+ * <b>NOTE: totals are not included in this conversion</b>
46
52
*
47
53
* @param visualizationObject which will be converted to {@link Execution}
48
- * @param visualizationClassGetter {@link Function} for fetching VisualizationClass, which is necessary for correct generation of {@link ResultSpec}
54
+ * @param visualizationClassGetter {@link Function} for fetching VisualizationClass,
55
+ * which is necessary for correct generation of {@link ResultSpec}
49
56
* @return {@link Execution} object
50
57
* @see #convertToExecution(VisualizationObject, VisualizationClass)
51
58
*/
@@ -59,6 +66,8 @@ public static Execution convertToExecution(final VisualizationObject visualizati
59
66
60
67
/**
61
68
* Generate Execution from Visualization object.
69
+ * <p>
70
+ * <b>NOTE: totals are not included in this conversion</b>
62
71
*
63
72
* @param visualizationObject which will be converted to {@link Execution}
64
73
* @param visualizationClass visualizationClass, which is necessary for correct generation of {@link ResultSpec}
@@ -75,27 +84,80 @@ public static Execution convertToExecution(final VisualizationObject visualizati
75
84
return new Execution (afm , resultSpec );
76
85
}
77
86
87
+ /**
88
+ * Generate Execution from Visualization object with totals included.
89
+ *
90
+ * @param visualizationObject which will be converted to {@link Execution}
91
+ * @param visualizationClassGetter {@link Function} for fetching VisualizationClass,
92
+ * which is necessary for correct generation of {@link ResultSpec}
93
+ * @return {@link Execution} object
94
+ * @see #convertToExecutionWithTotals(VisualizationObject, VisualizationClass)
95
+ */
96
+ public static Execution convertToExecutionWithTotals (final VisualizationObject visualizationObject ,
97
+ final Function <String , VisualizationClass > visualizationClassGetter ) {
98
+ notNull (visualizationObject , "visualizationObject" );
99
+ notNull (visualizationClassGetter , "visualizationClassGetter" );
100
+ return convertToExecutionWithTotals (visualizationObject ,
101
+ visualizationClassGetter .apply (visualizationObject .getVisualizationClassUri ()));
102
+ }
103
+
104
+ /**
105
+ * Generate Execution from Visualization object with totals included.
106
+ *
107
+ * @param visualizationObject which will be converted to {@link Execution}
108
+ * @param visualizationClass visualizationClass, which is necessary for correct generation of {@link ResultSpec}
109
+ * @return {@link Execution} object
110
+ * @see #convertToAfmWithNativeTotals(VisualizationObject)
111
+ * @see #convertToResultSpecWithTotals(VisualizationObject, VisualizationClass)
112
+ */
113
+ public static Execution convertToExecutionWithTotals (final VisualizationObject visualizationObject ,
114
+ final VisualizationClass visualizationClass ) {
115
+ notNull (visualizationObject , "visualizationObject" );
116
+ notNull (visualizationClass , "visualizationClass" );
117
+ ResultSpec resultSpec = convertToResultSpecWithTotals (visualizationObject , visualizationClass );
118
+ Afm afm = convertToAfmWithNativeTotals (visualizationObject );
119
+ return new Execution (afm , resultSpec );
120
+ }
121
+
78
122
/**
79
123
* Generate Afm from Visualization object.
124
+ * <p>
125
+ * <b>NOTE: native totals are not included in this conversion</b>
80
126
*
81
127
* @param visualizationObject which will be converted to {@link Execution}
82
128
* @return {@link Afm} object
83
129
*/
84
130
public static Afm convertToAfm (final VisualizationObject visualizationObject ) {
131
+ notNull (visualizationObject , "visualizationObject" );
132
+ final VisualizationObject visualizationObjectWithoutTotals = removeTotals (visualizationObject );
133
+ return convertToAfmWithNativeTotals (visualizationObjectWithoutTotals );
134
+ }
135
+
136
+ /**
137
+ * Generate Afm from Visualization object with native totals included.
138
+ *
139
+ * @param visualizationObject which will be converted to {@link Execution}
140
+ * @return {@link Afm} object
141
+ */
142
+ public static Afm convertToAfmWithNativeTotals (final VisualizationObject visualizationObject ) {
85
143
notNull (visualizationObject , "visualizationObject" );
86
144
final List <AttributeItem > attributes = convertAttributes (visualizationObject .getAttributes ());
87
145
final List <CompatibilityFilter > filters = convertFilters (visualizationObject .getFilters ());
88
146
final List <MeasureItem > measures = convertMeasures (visualizationObject .getMeasures ());
147
+ final List <NativeTotalItem > totals = convertNativeTotals (visualizationObject );
89
148
90
- return new Afm (attributes , filters , measures , null );
149
+ return new Afm (attributes , filters , measures , totals );
91
150
}
92
151
93
152
/**
94
153
* Generate ResultSpec from Visualization object. Currently {@link ResultSpec}'s {@link Dimension}s can be generated
95
154
* for table and four types of chart: bar, column, line and pie.
155
+ * <p>
156
+ * <b>NOTE: totals are not included in this conversion</b>
96
157
*
97
158
* @param visualizationObject which will be converted to {@link Execution}
98
- * @param visualizationClassGetter {@link Function} for fetching VisualizationClass, which is necessary for correct generation of {@link ResultSpec}
159
+ * @param visualizationClassGetter {@link Function} for fetching VisualizationClass,
160
+ * which is necessary for correct generation of {@link ResultSpec}
99
161
* @return {@link Execution} object
100
162
*/
101
163
public static ResultSpec convertToResultSpec (final VisualizationObject visualizationObject ,
@@ -109,6 +171,8 @@ public static ResultSpec convertToResultSpec(final VisualizationObject visualiza
109
171
/**
110
172
* Generate ResultSpec from Visualization object. Currently {@link ResultSpec}'s {@link Dimension}s can be generated
111
173
* for table and four types of chart: bar, column, line and pie.
174
+ * <p>
175
+ * <b>NOTE: totals are not included in this conversion</b>
112
176
*
113
177
* @param visualizationObject which will be converted to {@link Execution}
114
178
* @param visualizationClass VisualizationClass, which is necessary for correct generation of {@link ResultSpec}
@@ -118,6 +182,39 @@ public static ResultSpec convertToResultSpec(final VisualizationObject visualiza
118
182
final VisualizationClass visualizationClass ) {
119
183
notNull (visualizationObject , "visualizationObject" );
120
184
notNull (visualizationClass , "visualizationClass" );
185
+ final VisualizationObject visualizationObjectWithoutTotals = removeTotals (visualizationObject );
186
+ return convertToResultSpecWithTotals (visualizationObjectWithoutTotals , visualizationClass );
187
+ }
188
+
189
+ /**
190
+ * Generate ResultSpec from Visualization object with totals included. Currently {@link ResultSpec}'s {@link Dimension}s
191
+ * can be generated for table and four types of chart: bar, column, line and pie.
192
+ *
193
+ * @param visualizationObject which will be converted to {@link Execution}
194
+ * @param visualizationClassGetter {@link Function} for fetching VisualizationClass,
195
+ * which is necessary for correct generation of {@link ResultSpec}
196
+ * @return {@link Execution} object
197
+ */
198
+ public static ResultSpec convertToResultSpecWithTotals (final VisualizationObject visualizationObject ,
199
+ final Function <String , VisualizationClass > visualizationClassGetter ) {
200
+ notNull (visualizationObject , "visualizationObject" );
201
+ notNull (visualizationClassGetter , "visualizationClassGetter" );
202
+ return convertToResultSpecWithTotals (visualizationObject ,
203
+ visualizationClassGetter .apply (visualizationObject .getVisualizationClassUri ()));
204
+ }
205
+
206
+ /**
207
+ * Generate ResultSpec from Visualization object with totals included. Currently {@link ResultSpec}'s {@link Dimension}s
208
+ * can be generated for table and four types of chart: bar, column, line and pie.
209
+ *
210
+ * @param visualizationObject which will be converted to {@link Execution}
211
+ * @param visualizationClass VisualizationClass, which is necessary for correct generation of {@link ResultSpec}
212
+ * @return {@link Execution} object
213
+ */
214
+ public static ResultSpec convertToResultSpecWithTotals (final VisualizationObject visualizationObject ,
215
+ final VisualizationClass visualizationClass ) {
216
+ notNull (visualizationObject , "visualizationObject" );
217
+ notNull (visualizationClass , "visualizationClass" );
121
218
isTrue (visualizationObject .getVisualizationClassUri ().equals (visualizationClass .getUri ()),
122
219
"visualizationClass URI does not match the URI within visualizationObject, "
123
220
+ "you're trying to create ResultSpec for incompatible objects" );
@@ -144,6 +241,21 @@ static List<SortItem> parseSorting(final String properties) throws Exception {
144
241
return MAPPER .convertValue (nodeSortItems , mapType );
145
242
}
146
243
244
+ /**
245
+ * Creates a new {@link VisualizationObject} derived from the original one, with all "totals" removed from its buckets.
246
+ * This is to ensure backward compatibility in cases where totals were not previously handled.
247
+ *
248
+ * @param visualizationObject original {@link VisualizationObject}
249
+ * @return a new VisualizationObject derived from the original but without any totals in the buckets.
250
+ */
251
+ private static VisualizationObject removeTotals (final VisualizationObject visualizationObject ) {
252
+ final List <Bucket > bucketsWithoutTotals = visualizationObject .getBuckets ().stream ()
253
+ // create buckets without totals
254
+ .map (bucket -> new Bucket (bucket .getLocalIdentifier (), bucket .getItems ()))
255
+ .collect (toList ());
256
+ return visualizationObject .withBuckets (bucketsWithoutTotals );
257
+ }
258
+
147
259
private static List <Dimension > getDimensions (final VisualizationObject visualizationObject ,
148
260
final VisualizationType visualizationType ) {
149
261
switch (visualizationType ) {
@@ -216,12 +328,16 @@ private static List<Dimension> getDimensionsForTable(final VisualizationObject v
216
328
List <Dimension > dimensions = new ArrayList <>();
217
329
218
330
List <VisualizationAttribute > attributes = visualizationObject .getAttributes ();
331
+ List <TotalItem > totals = visualizationObject .getTotals ();
219
332
220
333
if (!attributes .isEmpty ()) {
221
- dimensions . add ( new Dimension (attributes .stream ()
334
+ final Dimension attributeDimension = new Dimension (attributes .stream ()
222
335
.map (VisualizationAttribute ::getLocalIdentifier )
223
- .collect (toList ())
224
- ));
336
+ .collect (toList ()));
337
+ if (!totals .isEmpty ()) {
338
+ attributeDimension .setTotals (new HashSet <>(totals ));
339
+ }
340
+ dimensions .add (attributeDimension );
225
341
} else {
226
342
dimensions .add (new Dimension (new ArrayList <>()));
227
343
}
@@ -316,4 +432,43 @@ private static <T> List<T> removeIrrelevantFilters(final List<T> filters) {
316
432
})
317
433
.collect (Collectors .toList ());
318
434
}
435
+
436
+ private static List <NativeTotalItem > convertNativeTotals (final VisualizationObject visualizationObject ) {
437
+ final List <Bucket > attributeBuckets = getAttributeBuckets (visualizationObject );
438
+ final List <String > attributeIds = getIdsFromAttributeBuckets (attributeBuckets );
439
+ return attributeBuckets .stream ()
440
+ .filter (bucket -> bucket .getTotals () != null )
441
+ .flatMap (bucket -> bucket .getTotals ().stream ())
442
+ .filter (totalItem -> isNativeTotal (totalItem ) && attributeIds .contains (totalItem .getAttributeIdentifier ()))
443
+ .map (totalItem -> convertToNativeTotalItem (totalItem , attributeIds ))
444
+ .collect (toList ());
445
+ }
446
+
447
+ private static NativeTotalItem convertToNativeTotalItem (TotalItem totalItem , List <String > attributeIds ) {
448
+ final int attributeIdx = attributeIds .indexOf (totalItem .getAttributeIdentifier ());
449
+ return new NativeTotalItem (
450
+ totalItem .getMeasureIdentifier (),
451
+ new ArrayList <>(attributeIds .subList (0 , attributeIdx ))
452
+ );
453
+ }
454
+
455
+ private static List <Bucket > getAttributeBuckets (final VisualizationObject visualizationObject ) {
456
+ return visualizationObject .getBuckets ().stream ()
457
+ .filter (bucket -> bucket .getItems ().stream ().allMatch (AttributeItem .class ::isInstance ))
458
+ .collect (toList ());
459
+ }
460
+
461
+ private static List <String > getIdsFromAttributeBuckets (final List <Bucket > attributeBuckets ) {
462
+ return attributeBuckets .stream ()
463
+ .flatMap (bucket ->
464
+ bucket .getItems ().stream ()
465
+ .map (AttributeItem .class ::cast )
466
+ .map (AttributeItem ::getLocalIdentifier )
467
+ )
468
+ .collect (toList ());
469
+ }
470
+
471
+ private static boolean isNativeTotal (TotalItem totalItem ) {
472
+ return totalItem .getType () != null && Total .NAT .name ().equals (totalItem .getType ().toUpperCase ());
473
+ }
319
474
}
0 commit comments