1111import com .gooddata .sdk .model .executeafm .Execution ;
1212import com .gooddata .sdk .model .executeafm .afm .Afm ;
1313import com .gooddata .sdk .model .executeafm .afm .AttributeItem ;
14+ import com .gooddata .sdk .model .executeafm .afm .NativeTotalItem ;
1415import com .gooddata .sdk .model .executeafm .afm .filter .CompatibilityFilter ;
1516import com .gooddata .sdk .model .executeafm .afm .filter .DateFilter ;
1617import com .gooddata .sdk .model .executeafm .afm .filter .ExtendedFilter ;
2425import com .gooddata .sdk .model .executeafm .resultspec .Dimension ;
2526import com .gooddata .sdk .model .executeafm .resultspec .ResultSpec ;
2627import 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 ;
2730
2831import java .util .ArrayList ;
32+ import java .util .HashSet ;
2933import java .util .List ;
3034import java .util .function .Function ;
3135import java .util .stream .Collectors ;
@@ -43,9 +47,12 @@ public abstract class VisualizationConverter {
4347
4448 /**
4549 * Generate Execution from Visualization object.
50+ * <p>
51+ * <b>NOTE: totals are not included in this conversion</b>
4652 *
4753 * @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}
4956 * @return {@link Execution} object
5057 * @see #convertToExecution(VisualizationObject, VisualizationClass)
5158 */
@@ -59,6 +66,8 @@ public static Execution convertToExecution(final VisualizationObject visualizati
5966
6067 /**
6168 * Generate Execution from Visualization object.
69+ * <p>
70+ * <b>NOTE: totals are not included in this conversion</b>
6271 *
6372 * @param visualizationObject which will be converted to {@link Execution}
6473 * @param visualizationClass visualizationClass, which is necessary for correct generation of {@link ResultSpec}
@@ -75,27 +84,80 @@ public static Execution convertToExecution(final VisualizationObject visualizati
7584 return new Execution (afm , resultSpec );
7685 }
7786
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+
78122 /**
79123 * Generate Afm from Visualization object.
124+ * <p>
125+ * <b>NOTE: native totals are not included in this conversion</b>
80126 *
81127 * @param visualizationObject which will be converted to {@link Execution}
82128 * @return {@link Afm} object
83129 */
84130 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 ) {
85143 notNull (visualizationObject , "visualizationObject" );
86144 final List <AttributeItem > attributes = convertAttributes (visualizationObject .getAttributes ());
87145 final List <CompatibilityFilter > filters = convertFilters (visualizationObject .getFilters ());
88146 final List <MeasureItem > measures = convertMeasures (visualizationObject .getMeasures ());
147+ final List <NativeTotalItem > totals = convertNativeTotals (visualizationObject );
89148
90- return new Afm (attributes , filters , measures , null );
149+ return new Afm (attributes , filters , measures , totals );
91150 }
92151
93152 /**
94153 * Generate ResultSpec from Visualization object. Currently {@link ResultSpec}'s {@link Dimension}s can be generated
95154 * 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>
96157 *
97158 * @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}
99161 * @return {@link Execution} object
100162 */
101163 public static ResultSpec convertToResultSpec (final VisualizationObject visualizationObject ,
@@ -109,6 +171,8 @@ public static ResultSpec convertToResultSpec(final VisualizationObject visualiza
109171 /**
110172 * Generate ResultSpec from Visualization object. Currently {@link ResultSpec}'s {@link Dimension}s can be generated
111173 * 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>
112176 *
113177 * @param visualizationObject which will be converted to {@link Execution}
114178 * @param visualizationClass VisualizationClass, which is necessary for correct generation of {@link ResultSpec}
@@ -118,6 +182,39 @@ public static ResultSpec convertToResultSpec(final VisualizationObject visualiza
118182 final VisualizationClass visualizationClass ) {
119183 notNull (visualizationObject , "visualizationObject" );
120184 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" );
121218 isTrue (visualizationObject .getVisualizationClassUri ().equals (visualizationClass .getUri ()),
122219 "visualizationClass URI does not match the URI within visualizationObject, "
123220 + "you're trying to create ResultSpec for incompatible objects" );
@@ -144,6 +241,21 @@ static List<SortItem> parseSorting(final String properties) throws Exception {
144241 return MAPPER .convertValue (nodeSortItems , mapType );
145242 }
146243
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+
147259 private static List <Dimension > getDimensions (final VisualizationObject visualizationObject ,
148260 final VisualizationType visualizationType ) {
149261 switch (visualizationType ) {
@@ -216,12 +328,16 @@ private static List<Dimension> getDimensionsForTable(final VisualizationObject v
216328 List <Dimension > dimensions = new ArrayList <>();
217329
218330 List <VisualizationAttribute > attributes = visualizationObject .getAttributes ();
331+ List <TotalItem > totals = visualizationObject .getTotals ();
219332
220333 if (!attributes .isEmpty ()) {
221- dimensions . add ( new Dimension (attributes .stream ()
334+ final Dimension attributeDimension = new Dimension (attributes .stream ()
222335 .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 );
225341 } else {
226342 dimensions .add (new Dimension (new ArrayList <>()));
227343 }
@@ -316,4 +432,43 @@ private static <T> List<T> removeIrrelevantFilters(final List<T> filters) {
316432 })
317433 .collect (Collectors .toList ());
318434 }
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+ }
319474}
0 commit comments