1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6  */
7 
8 #include "xlsx_pivot_context.hpp"
9 #include "ooxml_namespace_types.hpp"
10 #include "ooxml_token_constants.hpp"
11 #include "xml_context_global.hpp"
12 #include "session_context.hpp"
13 #include "xlsx_types.hpp"
14 
15 #include "orcus/measurement.hpp"
16 #include "orcus/spreadsheet/import_interface_pivot.hpp"
17 
18 #include <iostream>
19 #include <boost/optional.hpp>
20 #include <mdds/sorted_string_map.hpp>
21 
22 using namespace std;
23 
24 namespace orcus {
25 
26 namespace {
27 
28 typedef mdds::sorted_string_map<xlsx_pivot_cache_def_context::source_type> pc_source_type;
29 
30 // Keys must be sorted.
31 pc_source_type::entry pc_source_entries[] = {
32     { ORCUS_ASCII("consolidation"), xlsx_pivot_cache_def_context::source_type::consolidation },
33     { ORCUS_ASCII("external"),      xlsx_pivot_cache_def_context::source_type::external      },
34     { ORCUS_ASCII("scenario"),      xlsx_pivot_cache_def_context::source_type::scenario      },
35     { ORCUS_ASCII("worksheet"),     xlsx_pivot_cache_def_context::source_type::worksheet     },
36 };
37 
get_pc_source_map()38 const pc_source_type& get_pc_source_map()
39 {
40     static pc_source_type source_map(
41         pc_source_entries,
42         sizeof(pc_source_entries)/sizeof(pc_source_entries[0]),
43         xlsx_pivot_cache_def_context::source_type::unknown);
44 
45     return source_map;
46 }
47 
48 }
49 
xlsx_pivot_cache_def_context(session_context & cxt,const tokens & tokens,spreadsheet::iface::import_pivot_cache_definition & pcache,spreadsheet::pivot_cache_id_t pcache_id)50 xlsx_pivot_cache_def_context::xlsx_pivot_cache_def_context(
51     session_context& cxt, const tokens& tokens,
52     spreadsheet::iface::import_pivot_cache_definition& pcache,
53     spreadsheet::pivot_cache_id_t pcache_id) :
54     xml_context_base(cxt, tokens), m_pcache(pcache), m_pcache_id(pcache_id) {}
55 
can_handle_element(xmlns_id_t,xml_token_t) const56 bool xlsx_pivot_cache_def_context::can_handle_element(xmlns_id_t /*ns*/, xml_token_t /*name*/) const
57 {
58     return true;
59 }
60 
create_child_context(xmlns_id_t,xml_token_t)61 xml_context_base* xlsx_pivot_cache_def_context::create_child_context(xmlns_id_t /*ns*/, xml_token_t /*name*/)
62 {
63     return nullptr;
64 }
65 
end_child_context(xmlns_id_t,xml_token_t,xml_context_base *)66 void xlsx_pivot_cache_def_context::end_child_context(xmlns_id_t /*ns*/, xml_token_t /*name*/, xml_context_base* /*child*/)
67 {
68 }
69 
start_element(xmlns_id_t ns,xml_token_t name,const::std::vector<xml_token_attr_t> & attrs)70 void xlsx_pivot_cache_def_context::start_element(xmlns_id_t ns, xml_token_t name, const::std::vector<xml_token_attr_t>& attrs)
71 {
72     xml_token_pair_t parent = push_stack(ns, name);
73     if (ns != NS_ooxml_xlsx)
74         return;
75 
76     switch (name)
77     {
78         case XML_pivotCacheDefinition:
79         {
80             xml_element_expected(parent, XMLNS_UNKNOWN_ID, XML_UNKNOWN_TOKEN);
81 
82             pstring refreshed_by;
83             pstring rid;
84             long record_count = -1;
85 
86             for_each(attrs.begin(), attrs.end(),
87                 [&](const xml_token_attr_t& attr)
88                 {
89                     if (!attr.ns || attr.ns == NS_ooxml_xlsx)
90                     {
91                         switch (attr.name)
92                         {
93                             case XML_refreshedBy:
94                                 refreshed_by = attr.value;
95                             break;
96                             case XML_recordCount:
97                                 record_count = to_long(attr.value);
98                             break;
99                             default:
100                                 ;
101                         }
102                     }
103                     else if (attr.ns == NS_ooxml_r)
104                     {
105                         switch (attr.name)
106                         {
107                             case XML_id:
108                                 // relation id for its cache record.
109                                 rid = attr.value;
110                             break;
111                             default:
112                                 ;
113                         }
114                     }
115                 }
116             );
117 
118             if (get_config().debug)
119             {
120                 cout << "---" << endl;
121                 cout << "pivot cache definition" << endl;
122                 cout << "refreshed by: " << refreshed_by << endl;
123                 cout << "record count: " << record_count << endl;
124                 cout << "rid: " << rid << endl;
125             }
126 
127             if (!rid.empty())
128             {
129                 // The rid string here must be persistent beyond the current
130                 // context.
131                 rid = get_session_context().m_string_pool.intern(rid).first;
132 
133                 m_pcache_info.data.insert(
134                     opc_rel_extras_t::map_type::value_type(
135                         rid, orcus::make_unique<xlsx_rel_pivot_cache_record_info>(m_pcache_id)));
136             }
137 
138             break;
139         }
140         case XML_cacheSource:
141         {
142             xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotCacheDefinition);
143 
144             pstring source_type_s;
145 
146             for_each(attrs.begin(), attrs.end(),
147                 [&](const xml_token_attr_t& attr)
148                 {
149                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
150                         return;
151 
152                     switch (attr.name)
153                     {
154                         case XML_type:
155                             m_source_type =
156                                 get_pc_source_map().find(attr.value.get(), attr.value.size());
157 
158                             source_type_s = attr.value;
159                             break;
160                         default:
161                             ;
162                     }
163                 }
164             );
165 
166             if (get_config().debug)
167                 cout << "type: " << source_type_s << endl;
168 
169             break;
170         }
171         case XML_worksheetSource:
172         {
173             xml_element_expected(parent, NS_ooxml_xlsx, XML_cacheSource);
174             if (m_source_type != source_type::worksheet)
175                 throw xml_structure_error(
176                     "worksheetSource element encountered while the source type is not worksheet.");
177 
178             pstring ref, sheet_name, table_name;
179 
180             for_each(attrs.begin(), attrs.end(),
181                 [&](const xml_token_attr_t& attr)
182                 {
183                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
184                         return;
185 
186                     switch (attr.name)
187                     {
188                         case XML_ref:
189                             ref = attr.value;
190                             break;
191                         case XML_sheet:
192                             sheet_name = attr.value;
193                             break;
194                         case XML_name:
195                             table_name = attr.value;
196                             break;
197                         default:
198                             ;
199                     }
200                 }
201             );
202 
203             if (get_config().debug)
204             {
205                 cout << "table: " << table_name << endl;
206                 cout << "ref: " << ref << endl;
207                 cout << "sheet: " << sheet_name << endl;
208             }
209 
210             if (!table_name.empty())
211                 m_pcache.set_worksheet_source(table_name.data(), table_name.size());
212             else
213                 m_pcache.set_worksheet_source(
214                     ref.get(), ref.size(), sheet_name.get(), sheet_name.size());
215             break;
216         }
217         case XML_cacheFields:
218         {
219             xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotCacheDefinition);
220             single_long_attr_getter func(NS_ooxml_xlsx, XML_count);
221             long field_count = for_each(attrs.begin(), attrs.end(), func).get_value();
222 
223             if (get_config().debug)
224                 cout << "field count: " << field_count << endl;
225 
226             if (field_count < 0)
227                 throw xml_structure_error("field count of a pivot cache must be positive.");
228 
229             m_pcache.set_field_count(field_count);
230             break;
231         }
232         case XML_cacheField:
233         {
234             xml_element_expected(parent, NS_ooxml_xlsx, XML_cacheFields);
235 
236             pstring field_name;
237             long numfmt_id = -1;
238 
239             for_each(attrs.begin(), attrs.end(),
240                 [&](const xml_token_attr_t& attr)
241                 {
242                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
243                         return;
244 
245                     switch (attr.name)
246                     {
247                         case XML_name:
248                             field_name = attr.value;
249                         break;
250                         case XML_numFmtId:
251                             numfmt_id = to_long(attr.value);
252                         break;
253                         default:
254                             ;
255                     }
256                 }
257             );
258 
259             // TODO : Handle number format ID here.
260             m_pcache.set_field_name(field_name.get(), field_name.size());
261 
262             if (get_config().debug)
263             {
264                 cout << "* name: " << field_name << endl;
265                 cout << "  number format id: " << numfmt_id << endl;
266             }
267             break;
268         }
269         case XML_fieldGroup:
270         {
271             xml_element_expected(parent, NS_ooxml_xlsx, XML_cacheField);
272             long group_parent = -1;
273             long group_base = -1;
274 
275             for_each(attrs.begin(), attrs.end(),
276                 [&](const xml_token_attr_t& attr)
277                 {
278                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
279                         return;
280 
281                     switch (attr.name)
282                     {
283                         case XML_par:
284                             group_parent = to_long(attr.value);
285                             break;
286                         case XML_base:
287                             group_base = to_long(attr.value);
288                             break;
289                         default:
290                             ;
291                     }
292                 }
293             );
294 
295             if (get_config().debug)
296             {
297                 if (group_parent >= 0)
298                     cout << "  * group parent index: " << group_parent << endl;
299                 if (group_base >= 0)
300                     cout << "  * group base index: " << group_base << endl;
301             }
302 
303             if (group_base >= 0)
304             {
305                 // This is a group field.
306                 m_pcache_field_group = m_pcache.create_field_group(group_base);
307             }
308             break;
309         }
310         case XML_discretePr:
311         {
312             xml_element_expected(parent, NS_ooxml_xlsx, XML_fieldGroup);
313 
314             long count = -1;
315 
316             for_each(attrs.begin(), attrs.end(),
317                 [&](const xml_token_attr_t& attr)
318                 {
319                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
320                         return;
321 
322                     switch (attr.name)
323                     {
324                         case XML_count:
325                             count = to_long(attr.value);
326                             break;
327                         default:
328                             ;
329                     }
330                 }
331             );
332 
333             if (get_config().debug)
334                 cout << "  * group child member count: " << count << endl;
335 
336             break;
337         }
338         case XML_rangePr:
339         {
340             xml_element_expected(parent, NS_ooxml_xlsx, XML_fieldGroup);
341 
342             bool auto_start = true;
343             bool auto_end = true;
344             double start = 0.0;
345             double end = 0.0;
346             double interval = 1.0;
347 
348             boost::optional<date_time_t> start_date;
349             boost::optional<date_time_t> end_date;
350 
351             // Default group-by type appears to be 'range'.
352             spreadsheet::pivot_cache_group_by_t group_by =
353                 spreadsheet::pivot_cache_group_by_t::range;
354 
355             for_each(attrs.begin(), attrs.end(),
356                 [&](const xml_token_attr_t& attr)
357                 {
358                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
359                         return;
360 
361                     switch (attr.name)
362                     {
363                         case XML_autoStart:
364                             auto_start = to_bool(attr.value);
365                             break;
366                         case XML_autoEnd:
367                             auto_end = to_bool(attr.value);
368                             break;
369                         case XML_startNum:
370                             start = to_double(attr.value);
371                             break;
372                         case XML_endNum:
373                             end = to_double(attr.value);
374                             break;
375                         case XML_groupInterval:
376                             interval = to_double(attr.value);
377                             break;
378                         case XML_startDate:
379                             start_date = to_date_time(attr.value);
380                             break;
381                         case XML_endDate:
382                             end_date = to_date_time(attr.value);
383                             break;
384                         case XML_groupBy:
385                             group_by = spreadsheet::to_pivot_cache_group_by_enum(
386                                 attr.value.get(), attr.value.size());
387                             break;
388                         default:
389                             ;
390                     }
391                 }
392             );
393 
394             // Pass the values to the interface.
395             m_pcache_field_group->set_range_grouping_type(group_by);
396             m_pcache_field_group->set_range_auto_start(auto_start);
397             m_pcache_field_group->set_range_auto_end(auto_end);
398             m_pcache_field_group->set_range_start_number(start);
399             m_pcache_field_group->set_range_end_number(end);
400             m_pcache_field_group->set_range_interval(interval);
401 
402             if (start_date)
403                 m_pcache_field_group->set_range_start_date(*start_date);
404             if (end_date)
405                 m_pcache_field_group->set_range_end_date(*end_date);
406 
407             if (get_config().debug)
408             {
409                 cout << "  auto start: " << auto_start << endl;
410                 cout << "  auto end: " << auto_end << endl;
411                 cout << "  start: " << start << endl;
412                 cout << "  end: " << end << endl;
413                 cout << "  interval: " << interval << endl;
414 
415                 if (start_date)
416                     cout << "start date: " << *start_date << endl;
417                 if (end_date)
418                     cout << "end date: " << *end_date << endl;
419             }
420 
421             break;
422         }
423         case XML_sharedItems:
424         {
425             start_element_shared_items(parent, attrs);
426             break;
427         }
428         case XML_groupItems:
429         {
430             xml_element_expected(parent, NS_ooxml_xlsx, XML_fieldGroup);
431 
432             long count = -1;
433 
434             for_each(attrs.begin(), attrs.end(),
435                 [&](const xml_token_attr_t& attr)
436                 {
437                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
438                         return;
439 
440                     switch (attr.name)
441                     {
442                         case XML_count:
443                             count = to_long(attr.value);
444                             break;
445                         default:
446                             ;
447                     }
448                 }
449             );
450 
451             if (get_config().debug)
452                 cout << "  * group member count: " << count << endl;
453 
454             break;
455         }
456         case XML_s:
457         {
458             start_element_s(parent, attrs);
459             break;
460         }
461         case XML_n:
462         {
463             start_element_n(parent, attrs);
464             break;
465         }
466         case XML_d:
467         {
468             start_element_d(parent, attrs);
469             break;
470         }
471         case XML_e:
472         {
473             start_element_e(parent, attrs);
474             break;
475         }
476         case XML_x:
477         {
478             const xml_elem_set_t expected = {
479                 { NS_ooxml_xlsx, XML_discretePr },
480                 { NS_ooxml_xlsx, XML_reference },
481             };
482             xml_element_expected(parent, expected);
483 
484             long index = -1;
485 
486             for_each(attrs.begin(), attrs.end(),
487                 [&](const xml_token_attr_t& attr)
488                 {
489                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
490                         return;
491 
492                     switch (attr.name)
493                     {
494                         case XML_v:
495                             index = to_long(attr.value);
496                             break;
497                         default:
498                             ;
499                     }
500                 }
501             );
502 
503             if (index < 0)
504                 throw xml_structure_error("element 'x' without a required attribute 'v'.");
505 
506             if (get_config().debug)
507                 cout << "    * index = " << index << endl;
508 
509             if (m_pcache_field_group)
510                 m_pcache_field_group->link_base_to_group_items(index);
511 
512             break;
513         }
514         default:
515             warn_unhandled();
516     }
517 }
518 
end_element(xmlns_id_t ns,xml_token_t name)519 bool xlsx_pivot_cache_def_context::end_element(xmlns_id_t ns, xml_token_t name)
520 {
521     if (ns == NS_ooxml_xlsx)
522     {
523         switch (name)
524         {
525             case XML_pivotCacheDefinition:
526             {
527                 m_pcache.commit();
528                 break;
529             }
530             case XML_cacheField:
531             {
532                 m_pcache.commit_field();
533                 m_pcache_field_group = nullptr;
534                 break;
535             }
536             case XML_discretePr:
537             {
538                 break;
539             }
540             case XML_fieldGroup:
541             {
542                 if (m_pcache_field_group)
543                     m_pcache_field_group->commit();
544                 break;
545             }
546             case XML_s:
547                 end_element_s();
548                 break;
549             case XML_n:
550                 end_element_n();
551                 break;
552             case XML_d:
553                 end_element_d();
554                 break;
555             case XML_e:
556                 end_element_e();
557                 break;
558             default:
559                 ;
560         }
561     }
562 
563     return pop_stack(ns, name);
564 }
565 
characters(const pstring &,bool)566 void xlsx_pivot_cache_def_context::characters(const pstring& /*str*/, bool /*transient*/)
567 {
568 }
569 
pop_rel_extras()570 opc_rel_extras_t xlsx_pivot_cache_def_context::pop_rel_extras()
571 {
572     return std::move(m_pcache_info);
573 }
574 
start_element_s(const xml_token_pair_t & parent,const std::vector<xml_token_attr_t> & attrs)575 void xlsx_pivot_cache_def_context::start_element_s(
576     const xml_token_pair_t& parent, const std::vector<xml_token_attr_t>& attrs)
577 {
578     if (parent.first != NS_ooxml_xlsx)
579     {
580         warn_unhandled();
581         return;
582     }
583 
584     pstring value;
585 
586     for_each(attrs.begin(), attrs.end(),
587         [&](const xml_token_attr_t& attr)
588         {
589             if (attr.ns && attr.ns != NS_ooxml_xlsx)
590                 return;
591 
592             switch (attr.name)
593             {
594                 case XML_v:
595                     value = attr.value;
596                 break;
597                 default:
598                     ;
599             }
600         }
601     );
602 
603     switch (parent.second)
604     {
605         case XML_sharedItems:
606         {
607             // regular (non-group) field member name.
608 
609             if (get_config().debug)
610                 cout << "    * field member: " << value << endl;
611 
612             m_field_item_used = true;
613             m_pcache.set_field_item_string(value.get(), value.size());
614             break;
615         }
616         case XML_groupItems:
617         {
618             // group field member name.
619 
620             if (get_config().debug)
621                 cout << "    * group field member: " << value << endl;
622 
623             m_field_item_used = true;
624             if (m_pcache_field_group)
625                 m_pcache_field_group->set_field_item_string(value.get(), value.size());
626             break;
627         }
628         default:
629             warn_unhandled();
630     }
631 }
632 
end_element_s()633 void xlsx_pivot_cache_def_context::end_element_s()
634 {
635     const xml_token_pair_t& parent = get_parent_element();
636     if (parent.first != NS_ooxml_xlsx)
637         return;
638 
639     switch (parent.second)
640     {
641         case XML_sharedItems:
642         {
643             if (m_field_item_used)
644                 m_pcache.commit_field_item();
645             break;
646         }
647         case XML_groupItems:
648         {
649             if (m_pcache_field_group && m_field_item_used)
650                 m_pcache_field_group->commit_field_item();
651             break;
652         }
653         default:
654             ;
655     }
656 }
657 
start_element_n(const xml_token_pair_t & parent,const std::vector<xml_token_attr_t> & attrs)658 void xlsx_pivot_cache_def_context::start_element_n(
659     const xml_token_pair_t& parent, const std::vector<xml_token_attr_t>& attrs)
660 {
661     if (parent.first != NS_ooxml_xlsx)
662     {
663         warn_unhandled();
664         return;
665     }
666 
667     switch (parent.second)
668     {
669         case XML_sharedItems:
670         {
671             // numeric item of a cache field.
672             double value = 0.0;
673             m_field_item_used = true;
674 
675             for_each(attrs.begin(), attrs.end(),
676                 [&](const xml_token_attr_t& attr)
677                 {
678                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
679                         return;
680 
681                     switch (attr.name)
682                     {
683                         case XML_v:
684                             value = to_double(attr.value);
685                         break;
686                         case XML_u:
687                             // flag for unused item.
688                             m_field_item_used = !to_bool(attr.value);
689                         default:
690                             ;
691                     }
692                 }
693             );
694 
695             if (get_config().debug)
696             {
697                 cout << "  * n: " << value;
698                 if (!m_field_item_used)
699                     cout << " (unused)";
700                 cout << endl;
701 
702             }
703 
704             if (m_field_item_used)
705                 m_pcache.set_field_item_numeric(value);
706 
707             break;
708         }
709         default:
710             warn_unhandled();
711     }
712 }
713 
end_element_n()714 void xlsx_pivot_cache_def_context::end_element_n()
715 {
716     const xml_token_pair_t& parent = get_parent_element();
717     if (parent.first != NS_ooxml_xlsx)
718         return;
719 
720     switch (parent.second)
721     {
722         case XML_sharedItems:
723         {
724             if (m_field_item_used)
725                 m_pcache.commit_field_item();
726             break;
727         }
728         default:
729             ;
730     }
731 }
732 
start_element_d(const xml_token_pair_t & parent,const std::vector<xml_token_attr_t> & attrs)733 void xlsx_pivot_cache_def_context::start_element_d(
734     const xml_token_pair_t& parent, const std::vector<xml_token_attr_t>& attrs)
735 {
736     if (parent.first != NS_ooxml_xlsx)
737     {
738         warn_unhandled();
739         return;
740     }
741 
742     switch (parent.second)
743     {
744         case XML_sharedItems:
745         {
746             // date item of a cache field.
747             date_time_t dt;
748             m_field_item_used = true;
749 
750             for_each(attrs.begin(), attrs.end(),
751                 [&](const xml_token_attr_t& attr)
752                 {
753                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
754                         return;
755 
756                     switch (attr.name)
757                     {
758                         case XML_v:
759                             dt = to_date_time(attr.value);
760                         break;
761                         case XML_u:
762                             // flag for unused item.
763                             m_field_item_used = !to_bool(attr.value);
764                         default:
765                             ;
766                     }
767                 }
768             );
769 
770             if (get_config().debug)
771             {
772                 cout << "  * d: " << dt;
773                 if (!m_field_item_used)
774                     cout << " (unused)";
775                 cout << endl;
776 
777             }
778 
779             if (m_field_item_used)
780                 m_pcache.set_field_item_date_time(dt);
781 
782             break;
783         }
784         default:
785             ;
786     }
787 }
788 
end_element_d()789 void xlsx_pivot_cache_def_context::end_element_d()
790 {
791     const xml_token_pair_t& parent = get_parent_element();
792     if (parent.first != NS_ooxml_xlsx)
793         return;
794 
795     switch (parent.second)
796     {
797         case XML_sharedItems:
798         {
799             if (m_field_item_used)
800                 m_pcache.commit_field_item();
801             break;
802         }
803         default:
804             ;
805     }
806 }
807 
start_element_e(const xml_token_pair_t & parent,const std::vector<xml_token_attr_t> & attrs)808 void xlsx_pivot_cache_def_context::start_element_e(
809     const xml_token_pair_t& parent, const std::vector<xml_token_attr_t>& attrs)
810 {
811     if (parent.first != NS_ooxml_xlsx)
812     {
813         warn_unhandled();
814         return;
815     }
816 
817     switch (parent.second)
818     {
819         case XML_sharedItems:
820         {
821             // error value item of a cache field.
822             spreadsheet::error_value_t ev = spreadsheet::error_value_t::unknown;
823             m_field_item_used = true;
824 
825             for_each(attrs.begin(), attrs.end(),
826                 [&](const xml_token_attr_t& attr)
827                 {
828                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
829                         return;
830 
831                     switch (attr.name)
832                     {
833                         case XML_v:
834                             ev = spreadsheet::to_error_value_enum(attr.value.get(), attr.value.size());
835                         break;
836                         case XML_u:
837                             // flag for unused item.
838                             m_field_item_used = !to_bool(attr.value);
839                         default:
840                             ;
841                     }
842                 }
843             );
844 
845             if (get_config().debug)
846             {
847                 cout << "  * e: " << ev;
848                 if (!m_field_item_used)
849                     cout << " (unused)";
850                 cout << endl;
851             }
852 
853             if (m_field_item_used)
854                 m_pcache.set_field_item_error(ev);
855 
856             break;
857         }
858         default:
859             ;
860     }
861 }
862 
end_element_e()863 void xlsx_pivot_cache_def_context::end_element_e()
864 {
865     const xml_token_pair_t& parent = get_parent_element();
866     if (parent.first != NS_ooxml_xlsx)
867         return;
868 
869     switch (parent.second)
870     {
871         case XML_sharedItems:
872         {
873             if (m_field_item_used)
874                 m_pcache.commit_field_item();
875             break;
876         }
877         default:
878             ;
879     }
880 }
881 
start_element_shared_items(const xml_token_pair_t & parent,const std::vector<xml_token_attr_t> & attrs)882 void xlsx_pivot_cache_def_context::start_element_shared_items(
883     const xml_token_pair_t& parent, const std::vector<xml_token_attr_t>& attrs)
884 {
885     xml_element_expected(parent, NS_ooxml_xlsx, XML_cacheField);
886 
887     // If "semi-mixed types" is set, the field contains text values and at
888     // least one other type.
889     bool semi_mixed_types = true;
890 
891     bool has_non_date = true;
892     bool has_date = false;
893     bool has_string = true;
894     bool has_blank = false;
895 
896     // If "mixed types" is set, the field contains more than one data types.
897     bool mixed_types = false;
898 
899     bool has_number = false;
900     bool has_integer = false;
901     bool has_long_text = false;
902 
903     long count = -1;
904     boost::optional<double> min_value;
905     boost::optional<double> max_value;
906     boost::optional<date_time_t> min_date;
907     boost::optional<date_time_t> max_date;
908 
909     for_each(attrs.begin(), attrs.end(),
910         [&](const xml_token_attr_t& attr)
911         {
912             if (attr.ns && attr.ns != NS_ooxml_xlsx)
913                 return;
914 
915             switch (attr.name)
916             {
917                 case XML_count:
918                     count = to_long(attr.value);
919                     break;
920                 case XML_containsMixedTypes:
921                     mixed_types = to_bool(attr.value);
922                     break;
923                 case XML_containsSemiMixedTypes:
924                     semi_mixed_types = to_bool(attr.value);
925                     break;
926                 case XML_containsNonDate:
927                     has_non_date = to_bool(attr.value);
928                     break;
929                 case XML_containsString:
930                     has_string = to_bool(attr.value);
931                     break;
932                 case XML_containsBlank:
933                     has_blank = to_bool(attr.value);
934                     break;
935                 case XML_containsNumber:
936                     has_number = to_bool(attr.value);
937                     break;
938                 case XML_containsInteger:
939                     has_integer = to_bool(attr.value);
940                     break;
941                 case XML_minValue:
942                     min_value = to_double(attr.value);
943                     break;
944                 case XML_maxValue:
945                     max_value = to_double(attr.value);
946                     break;
947                 case XML_minDate:
948                     min_date = to_date_time(attr.value);
949                     break;
950                 case XML_maxDate:
951                     max_date = to_date_time(attr.value);
952                     break;
953                 case XML_longText:
954                     has_long_text = to_bool(attr.value);
955                     break;
956                 default:
957                     ;
958             }
959         }
960     );
961 
962     if (min_value)
963         m_pcache.set_field_min_value(*min_value);
964 
965     if (max_value)
966         m_pcache.set_field_max_value(*max_value);
967 
968     if (min_date)
969         m_pcache.set_field_min_date(*min_date);
970 
971     if (max_date)
972         m_pcache.set_field_max_date(*max_date);
973 
974     if (get_config().debug)
975     {
976         cout << "  contains semi-mixed types: " << semi_mixed_types << endl;
977         cout << "  contains non-date: " << has_non_date << endl;
978         cout << "  contains date: " << has_date << endl;
979         cout << "  contains string: " << has_string << endl;
980         cout << "  contains blank: " << has_blank << endl;
981         cout << "  contains mixed types: " << mixed_types << endl;
982         cout << "  contains number: " << has_number << endl;
983         cout << "  contains integer: " << has_integer << endl;
984         cout << "  contains long text: " << has_long_text << endl;
985         cout << "  count: " << count << endl;
986 
987         if (min_value)
988             cout << "  min value: " << *min_value << endl;
989         if (max_value)
990             cout << "  max value: " << *max_value << endl;
991         if (min_date)
992             cout << "  min date: " << *min_date << endl;
993         if (max_date)
994             cout << "  max date: " << *max_date << endl;
995     }
996 }
997 
xlsx_pivot_cache_rec_context(session_context & cxt,const tokens & tokens,spreadsheet::iface::import_pivot_cache_records & pc_records)998 xlsx_pivot_cache_rec_context::xlsx_pivot_cache_rec_context(
999     session_context& cxt, const tokens& tokens,
1000     spreadsheet::iface::import_pivot_cache_records& pc_records) :
1001     xml_context_base(cxt, tokens),
1002     m_pc_records(pc_records) {}
1003 
can_handle_element(xmlns_id_t,xml_token_t) const1004 bool xlsx_pivot_cache_rec_context::can_handle_element(xmlns_id_t /*ns*/, xml_token_t /*name*/) const
1005 {
1006     return true;
1007 }
1008 
create_child_context(xmlns_id_t,xml_token_t)1009 xml_context_base* xlsx_pivot_cache_rec_context::create_child_context(xmlns_id_t /*ns*/, xml_token_t /*name*/)
1010 {
1011     return nullptr;
1012 }
1013 
end_child_context(xmlns_id_t,xml_token_t,xml_context_base *)1014 void xlsx_pivot_cache_rec_context::end_child_context(xmlns_id_t /*ns*/, xml_token_t /*name*/, xml_context_base* /*child*/)
1015 {
1016 }
1017 
start_element(xmlns_id_t ns,xml_token_t name,const::std::vector<xml_token_attr_t> & attrs)1018 void xlsx_pivot_cache_rec_context::start_element(xmlns_id_t ns, xml_token_t name, const::std::vector<xml_token_attr_t>& attrs)
1019 {
1020     xml_token_pair_t parent = push_stack(ns, name);
1021 
1022     if (ns != NS_ooxml_xlsx)
1023         return;
1024 
1025     switch (name)
1026     {
1027         case XML_pivotCacheRecords:
1028         {
1029             xml_element_expected(parent, XMLNS_UNKNOWN_ID, XML_UNKNOWN_TOKEN);
1030             long count = single_long_attr_getter::get(attrs, NS_ooxml_xlsx, XML_count);
1031 
1032             if (get_config().debug)
1033             {
1034                 cout << "---" << endl;
1035                 cout << "pivot cache record (count: " << count << ")" << endl;
1036             }
1037 
1038             m_pc_records.set_record_count(count);
1039             break;
1040         }
1041         case XML_r: // record
1042             xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotCacheRecords);
1043             if (get_config().debug)
1044                 cout << "* record" << endl;
1045 
1046             break;
1047         case XML_s: // character value
1048         {
1049             xml_element_expected(parent, NS_ooxml_xlsx, XML_r);
1050 
1051             pstring cv = single_attr_getter::get(attrs, NS_ooxml_xlsx, XML_v);
1052 
1053             if (get_config().debug)
1054                 cout << "  * s = '" << cv << "'" << endl;
1055 
1056             m_pc_records.append_record_value_character(cv.get(), cv.size());
1057             break;
1058         }
1059         case XML_x: // shared item index
1060         {
1061             xml_element_expected(parent, NS_ooxml_xlsx, XML_r);
1062             long v = single_long_attr_getter::get(attrs, NS_ooxml_xlsx, XML_v);
1063             if (get_config().debug)
1064                 cout << "  * x = " << v << endl;
1065 
1066             m_pc_records.append_record_value_shared_item(v);
1067             break;
1068         }
1069         case XML_n: // numeric
1070         {
1071             xml_element_expected(parent, NS_ooxml_xlsx, XML_r);
1072             double val = single_double_attr_getter::get(attrs, NS_ooxml_xlsx, XML_v);
1073             if (get_config().debug)
1074                 cout << "  * n = " << val << endl;
1075 
1076             m_pc_records.append_record_value_numeric(val);
1077             break;
1078         }
1079         case XML_e: // error value
1080         {
1081             pstring cv = single_attr_getter::get(attrs, NS_ooxml_xlsx, XML_v);
1082 
1083             if (get_config().debug)
1084                 cout << "  * e = " << cv << endl;
1085 
1086             break;
1087         }
1088         case XML_b: // boolean
1089         case XML_d: // date time
1090         case XML_m: // no value
1091         default:
1092             warn_unhandled();
1093     }
1094 }
1095 
end_element(xmlns_id_t ns,xml_token_t name)1096 bool xlsx_pivot_cache_rec_context::end_element(xmlns_id_t ns, xml_token_t name)
1097 {
1098     if (ns == NS_ooxml_xlsx)
1099     {
1100         switch (name)
1101         {
1102             case XML_pivotCacheRecords:
1103                 m_pc_records.commit();
1104                 break;
1105             case XML_r: // record
1106                 m_pc_records.commit_record();
1107                 break;
1108             default:
1109                 ;
1110         }
1111     }
1112 
1113     return pop_stack(ns, name);
1114 }
1115 
characters(const pstring &,bool)1116 void xlsx_pivot_cache_rec_context::characters(const pstring& /*str*/, bool /*transient*/)
1117 {
1118 }
1119 
xlsx_pivot_table_context(session_context & cxt,const tokens & tokens)1120 xlsx_pivot_table_context::xlsx_pivot_table_context(session_context& cxt, const tokens& tokens) :
1121     xml_context_base(cxt, tokens) {}
1122 
can_handle_element(xmlns_id_t,xml_token_t) const1123 bool xlsx_pivot_table_context::can_handle_element(xmlns_id_t /*ns*/, xml_token_t /*name*/) const
1124 {
1125     return true;
1126 }
1127 
create_child_context(xmlns_id_t,xml_token_t)1128 xml_context_base* xlsx_pivot_table_context::create_child_context(xmlns_id_t /*ns*/, xml_token_t /*name*/)
1129 {
1130     return nullptr;
1131 }
1132 
end_child_context(xmlns_id_t,xml_token_t,xml_context_base *)1133 void xlsx_pivot_table_context::end_child_context(xmlns_id_t /*ns*/, xml_token_t /*name*/, xml_context_base* /*child*/)
1134 {
1135 }
1136 
start_element(xmlns_id_t ns,xml_token_t name,const::std::vector<xml_token_attr_t> & attrs)1137 void xlsx_pivot_table_context::start_element(xmlns_id_t ns, xml_token_t name, const::std::vector<xml_token_attr_t>& attrs)
1138 {
1139     xml_token_pair_t parent = push_stack(ns, name);
1140     if (ns == NS_ooxml_xlsx)
1141     {
1142         switch (name)
1143         {
1144             case XML_pivotTableDefinition:
1145             {
1146                 xml_element_expected(parent, XMLNS_UNKNOWN_ID, XML_UNKNOWN_TOKEN);
1147                 if (get_config().debug)
1148                     cout << "---" << endl;
1149                 for (const xml_token_attr_t& attr : attrs)
1150                 {
1151                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
1152                         continue;
1153 
1154                     long v = 0;
1155                     bool b = false;
1156 
1157                     switch (attr.name)
1158                     {
1159                         case XML_name:
1160                             if (get_config().debug)
1161                                 cout << "name: " << attr.value << endl;
1162                             break;
1163                         case XML_cacheId:
1164                             v = to_long(attr.value);
1165                             if (get_config().debug)
1166                                 cout << "cache ID: " << v << endl;
1167                             break;
1168                         case XML_applyNumberFormats:
1169                             b = to_bool(attr.value);
1170                             if (get_config().debug)
1171                                 cout << "apply number formats: " << b << endl;
1172                             break;
1173                         case XML_applyBorderFormats:
1174                             b = to_bool(attr.value);
1175                             if (get_config().debug)
1176                                 cout << "apply border formats: " << b << endl;
1177                             break;
1178                         case XML_applyFontFormats:
1179                             b = to_bool(attr.value);
1180                             if (get_config().debug)
1181                                 cout << "apply font formats: " << b << endl;
1182                             break;
1183                         case XML_applyPatternFormats:
1184                             b = to_bool(attr.value);
1185                             if (get_config().debug)
1186                                 cout << "apply pattern formats: " << b << endl;
1187                             break;
1188                         case XML_applyAlignmentFormats:
1189                             b = to_bool(attr.value);
1190                             if (get_config().debug)
1191                                 cout << "apply alignment formats: " << b << endl;
1192                             break;
1193                         case XML_applyWidthHeightFormats:
1194                             b = to_bool(attr.value);
1195                             if (get_config().debug)
1196                                 cout << "apply width/height formats: " << b << endl;
1197                             break;
1198                         case XML_dataCaption:
1199                             if (get_config().debug)
1200                                 cout << "data caption: " << attr.value << endl;
1201                             break;
1202                         case XML_updatedVersion:
1203                             v = to_long(attr.value);
1204                             if (get_config().debug)
1205                                 cout << "updated version: " << v << endl;
1206                             break;
1207                         case XML_minRefreshableVersion:
1208                             v = to_long(attr.value);
1209                             if (get_config().debug)
1210                                 cout << "minimum refreshable version: " << v << endl;
1211                             break;
1212                         case XML_showCalcMbrs:
1213                             b = to_bool(attr.value);
1214                             if (get_config().debug)
1215                                 cout << "show calc members (?): " << b << endl;
1216                             break;
1217                         case XML_useAutoFormatting:
1218                             b = to_bool(attr.value);
1219                             if (get_config().debug)
1220                                 cout << "use auto formatting: " << b << endl;
1221                             break;
1222                         case XML_itemPrintTitles:
1223                             b = to_bool(attr.value);
1224                             if (get_config().debug)
1225                                 cout << "item print titles (?): " << b << endl;
1226                             break;
1227                         case XML_createdVersion:
1228                             v = to_long(attr.value);
1229                             if (get_config().debug)
1230                                 cout << "created version: " << v << endl;
1231                             break;
1232                         case XML_indent:
1233                             b = to_bool(attr.value);
1234                             if (get_config().debug)
1235                                 cout << "indent: " << b << endl;
1236                             break;
1237                         case XML_compact:
1238                             b = to_bool(attr.value);
1239                             if (get_config().debug)
1240                                 cout << "compact: " << b << endl;
1241                             break;
1242                         case XML_compactData:
1243                             b = to_bool(attr.value);
1244                             if (get_config().debug)
1245                                 cout << "compact data: " << b << endl;
1246                             break;
1247                         case XML_outline:
1248                             b = to_bool(attr.value);
1249                             if (get_config().debug)
1250                                 cout << "outline: " << b << endl;
1251                             break;
1252                         case XML_outlineData:
1253                             b = to_bool(attr.value);
1254                             if (get_config().debug)
1255                                 cout << "outline data: " << b << endl;
1256                             break;
1257                         case XML_gridDropZones:
1258                             b = to_bool(attr.value);
1259                             if (get_config().debug)
1260                                 cout << "grid drop zones: " << b << endl;
1261                             break;
1262                         case XML_multipleFieldFilters:
1263                             b = to_bool(attr.value);
1264                             if (get_config().debug)
1265                                 cout << "multiple field filters: " << b << endl;
1266                             break;
1267                         default:
1268                             ;
1269                     }
1270                 }
1271             }
1272             break;
1273             case XML_location:
1274             {
1275                 xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotTableDefinition);
1276 
1277                 for (const xml_token_attr_t& attr : attrs)
1278                 {
1279                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
1280                         continue;
1281 
1282                     long v = -1;
1283                     switch (attr.name)
1284                     {
1285                         case XML_ref:
1286                             if (get_config().debug)
1287                                 cout << "ref: " << attr.value << endl;
1288                             break;
1289                         case XML_firstHeaderRow:
1290                             v = to_long(attr.value);
1291                             if (get_config().debug)
1292                                 cout << "first header row: " << v << endl;
1293                             break;
1294                         case XML_firstDataRow:
1295                             v = to_long(attr.value);
1296                             if (get_config().debug)
1297                                 cout << "first data row: " << v << endl;
1298                             break;
1299                         case XML_firstDataCol:
1300                             v = to_long(attr.value);
1301                             if (get_config().debug)
1302                                 cout << "first data column: " << v << endl;
1303                             break;
1304                         default:
1305                             ;
1306                     }
1307                 }
1308             }
1309             break;
1310             case XML_pivotFields:
1311             {
1312                 // pivotFields and its child elements represent the visual
1313                 // appearances of the fields inside pivot table.
1314                 xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotTableDefinition);
1315                 size_t count = single_long_attr_getter::get(attrs, NS_ooxml_xlsx, XML_count);
1316                 if (get_config().debug)
1317                     cout << "field count: " << count << endl;
1318             }
1319             break;
1320             case XML_pivotField:
1321             {
1322                 xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotFields);
1323 
1324                 if (get_config().debug)
1325                     cout << "---" << endl;
1326 
1327                 for (const xml_token_attr_t& attr : attrs)
1328                 {
1329                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
1330                         continue;
1331 
1332                     switch (attr.name)
1333                     {
1334                         case XML_axis:
1335                             if (get_config().debug)
1336                                 cout << "  * axis: " << attr.value << endl;
1337                         break;
1338                         case XML_compact:
1339                         {
1340                             bool b = to_bool(attr.value);
1341                             if (get_config().debug)
1342                                 cout << "  * compact: " << b << endl;
1343                         }
1344                         break;
1345                         case XML_outline:
1346                         {
1347                             bool b = to_bool(attr.value);
1348                             if (get_config().debug)
1349                                 cout << "  * outline: " << b << endl;
1350                         }
1351                         break;
1352                         case XML_showAll:
1353                         {
1354                             bool b = to_bool(attr.value);
1355                             if (get_config().debug)
1356                                 cout << "  * show all: " << b << endl;
1357                         }
1358                         break;
1359                         case XML_dataField:
1360                         {
1361                             bool b = to_bool(attr.value);
1362                             if (get_config().debug)
1363                                 cout << "  * data field: " << b << endl;
1364                         }
1365                         break;
1366                         default:
1367                             ;
1368                     }
1369                 }
1370             }
1371             break;
1372             case XML_items:
1373             {
1374                 xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotField);
1375                 size_t count = single_long_attr_getter::get(attrs, NS_ooxml_xlsx, XML_count);
1376                 if (get_config().debug)
1377                     cout << "  * item count: " << count << endl;
1378             }
1379             break;
1380             case XML_item:
1381             {
1382                 xml_element_expected(parent, NS_ooxml_xlsx, XML_items);
1383 
1384                 for (const xml_token_attr_t& attr : attrs)
1385                 {
1386                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
1387                         continue;
1388 
1389                     switch (attr.name)
1390                     {
1391                         case XML_x:
1392                         {
1393                             // field item index as defined in the pivot cache.
1394                             long idx = to_long(attr.value);
1395                             if (get_config().debug)
1396                                 cout << "    * x = " << idx << endl;
1397                         }
1398                         break;
1399                         case XML_t:
1400                         {
1401                             // When the <item> element has attribute 't', it's subtotal or
1402                             // some sort of function item.  See 3.18.45 ST_ItemType
1403                             // (PivotItem Type) for possible values.
1404                             if (get_config().debug)
1405                                 cout << "    * type = " << attr.value << endl;
1406                         }
1407                         break;
1408                         default:
1409                             ;
1410                     }
1411                 }
1412             }
1413             break;
1414             case XML_rowFields:
1415             {
1416                 xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotTableDefinition);
1417                 size_t count = single_long_attr_getter::get(attrs, NS_ooxml_xlsx, XML_count);
1418                 if (get_config().debug)
1419                 {
1420                     cout << "---" << endl;
1421                     cout << "row field count: " << count << endl;
1422                 }
1423             }
1424             break;
1425             case XML_colFields:
1426             {
1427                 xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotTableDefinition);
1428                 size_t count = single_long_attr_getter::get(attrs, NS_ooxml_xlsx, XML_count);
1429                 if (get_config().debug)
1430                 {
1431                     cout << "---" << endl;
1432                     cout << "column field count: " << count << endl;
1433                 }
1434             }
1435             break;
1436             case XML_pageFields:
1437             {
1438                 xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotTableDefinition);
1439                 size_t count = single_long_attr_getter::get(attrs, NS_ooxml_xlsx, XML_count);
1440                 if (get_config().debug)
1441                 {
1442                     cout << "---" << endl;
1443                     cout << "page field count: " << count << endl;
1444                 }
1445             }
1446             break;
1447             case XML_pageField:
1448             {
1449                 xml_element_expected(parent, NS_ooxml_xlsx, XML_pageFields);
1450 
1451                 if (get_config().debug)
1452                     cout << "  * page field:";
1453 
1454                 for (const xml_token_attr_t& attr : attrs)
1455                 {
1456                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
1457                         continue;
1458 
1459                     switch (attr.name)
1460                     {
1461                         case XML_fld:
1462                         {
1463                             long fld = to_long(attr.value);
1464                             if (get_config().debug)
1465                                 cout << "field index = " << fld << "; ";
1466                             break;
1467                         }
1468                         case XML_item:
1469                         {
1470                             long item = to_long(attr.value);
1471                             if (get_config().debug)
1472                                 cout << "item index = " << item << "; ";
1473                             break;
1474                         }
1475                         case XML_hier:
1476                         {
1477                             long hier = to_long(attr.value);
1478                             // -1 if not applicable.
1479                             if (get_config().debug)
1480                                 cout << "OLAP hierarchy index = " << hier << "; ";
1481                             break;
1482                         }
1483                         default:
1484                             ;
1485                     }
1486                 }
1487 
1488                 if (get_config().debug)
1489                     cout << endl;
1490                 break;
1491             }
1492             case XML_field:
1493             {
1494                 xml_elem_stack_t expected;
1495                 expected.reserve(3);
1496                 expected.push_back(xml_token_pair_t(NS_ooxml_xlsx, XML_rowFields));
1497                 expected.push_back(xml_token_pair_t(NS_ooxml_xlsx, XML_colFields));
1498                 xml_element_expected(parent, expected);
1499 
1500                 // Index into the list of <pivotField> collection which is
1501                 // given earlier under the <pivotFields> element.  The value
1502                 // of -2 represents a special field that displays the list of
1503                 // data fields when the pivot table contains more than one
1504                 // data field.
1505                 long idx = single_long_attr_getter::get(attrs, NS_ooxml_xlsx, XML_x);
1506                 if (get_config().debug)
1507                     cout << "  * x = " << idx << endl;
1508             }
1509             break;
1510             case XML_dataFields:
1511             {
1512                 xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotTableDefinition);
1513                 size_t count = single_long_attr_getter::get(attrs, NS_ooxml_xlsx, XML_count);
1514                 if (get_config().debug)
1515                 {
1516                     cout << "---" << endl;
1517                     cout << "data field count: " << count << endl;
1518                 }
1519             }
1520             break;
1521             case XML_dataField:
1522             {
1523                 xml_element_expected(parent, NS_ooxml_xlsx, XML_dataFields);
1524 
1525                 if (get_config().debug)
1526                     cout << "  * data field: ";
1527 
1528                 for (const xml_token_attr_t& attr : attrs)
1529                 {
1530                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
1531                         continue;
1532 
1533                     switch (attr.name)
1534                     {
1535                         case XML_name:
1536                         {
1537                             if (get_config().debug)
1538                                 cout << "name = " << attr.value << "; ";
1539                             break;
1540                         }
1541                         case XML_fld:
1542                         {
1543                             long fld = to_long(attr.value);
1544                             if (get_config().debug)
1545                                 cout << "field = " << fld << "; ";
1546                             break;
1547                         }
1548                         case XML_baseField:
1549                         {
1550                             long fld = to_long(attr.value);
1551                             if (get_config().debug)
1552                                 cout << "base field = " << fld << "; ";
1553                             break;
1554                         }
1555                         case XML_baseItem:
1556                         {
1557                             long fld = to_long(attr.value);
1558                             if (get_config().debug)
1559                                 cout << "base item = " << fld << "; ";
1560                             break;
1561                         }
1562                         case XML_subtotal:
1563                         {
1564                             if (get_config().debug)
1565                                 cout << "subtotal = " << attr.value << "; ";
1566                             break;
1567                         }
1568                         default:
1569                             ;
1570                     }
1571                 }
1572 
1573                 if (get_config().debug)
1574                     cout << endl;
1575             }
1576             break;
1577             case XML_rowItems:
1578             {
1579                 // <rowItems> structure describes the displayed content of
1580                 // cells in the row field area.  Each <i> child element
1581                 // represents a single row.
1582                 xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotTableDefinition);
1583                 size_t count = single_long_attr_getter::get(attrs, NS_ooxml_xlsx, XML_count);
1584                 if (get_config().debug)
1585                 {
1586                     cout << "---" << endl;
1587                     cout << "row item count: " << count << endl;
1588                 }
1589             }
1590             break;
1591             case XML_colItems:
1592             {
1593                 xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotTableDefinition);
1594                 size_t count = single_long_attr_getter::get(attrs, NS_ooxml_xlsx, XML_count);
1595                 if (get_config().debug)
1596                 {
1597                     cout << "---" << endl;
1598                     cout << "column item count: " << count << endl;
1599                 }
1600             }
1601             break;
1602             case XML_i:
1603             {
1604                 xml_elem_stack_t expected;
1605                 expected.reserve(2);
1606                 expected.push_back(xml_token_pair_t(NS_ooxml_xlsx, XML_rowItems));
1607                 expected.push_back(xml_token_pair_t(NS_ooxml_xlsx, XML_colItems));
1608                 xml_element_expected(parent, expected);
1609 
1610                 if (get_config().debug)
1611                     cout << "---" << endl;
1612 
1613                 for (const xml_token_attr_t& attr : attrs)
1614                 {
1615                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
1616                         continue;
1617 
1618                     switch (attr.name)
1619                     {
1620                         case XML_t:
1621                         {
1622                             // total or subtotal function type.
1623                             if (get_config().debug)
1624                                 cout << "  * type = " << attr.value << endl;
1625                         }
1626                         break;
1627                         case XML_r:
1628                         {
1629                             // "repeated item count" which basically is the number of
1630                             // blank cells that occur after the preivous non-empty cell on
1631                             // the same row (in the classic layout mode).
1632                             long v = to_long(attr.value);
1633                             if (get_config().debug)
1634                                 cout << "  * repeat item count = " << v << endl;
1635                         }
1636                         break;
1637                         case XML_i:
1638                         {
1639                             // zero-based data field index in case of multiple data fields.
1640                             long v = to_long(attr.value);
1641                             if (get_config().debug)
1642                                 cout << "  * data field index = " << v << endl;
1643                         }
1644                         break;
1645                         default:
1646                             ;
1647                     }
1648                 }
1649             }
1650             break;
1651             case XML_x:
1652             {
1653                 if (parent.first != NS_ooxml_xlsx)
1654                 {
1655                     warn_unhandled();
1656                     break;
1657                 }
1658 
1659                 if (parent.second == XML_i)
1660                 {
1661                     long idx = single_long_attr_getter::get(attrs, NS_ooxml_xlsx, XML_v);
1662                     if (idx < 0)
1663                         // 0 is default when not set.
1664                         idx = 0;
1665 
1666                     if (get_config().debug)
1667                         cout << "  * v = " << idx << endl;
1668                     break;
1669                 }
1670 
1671                 warn_unhandled();
1672             }
1673             break;
1674             case XML_pivotTableStyleInfo:
1675             {
1676                 xml_element_expected(parent, NS_ooxml_xlsx, XML_pivotTableDefinition);
1677 
1678                 if (get_config().debug)
1679                 {
1680                     cout << "---" << endl;
1681                     cout << "* style info: ";
1682                 }
1683 
1684                 for (const xml_token_attr_t& attr : attrs)
1685                 {
1686                     if (attr.ns && attr.ns != NS_ooxml_xlsx)
1687                         continue;
1688 
1689                     bool b = false;
1690 
1691                     switch (attr.name)
1692                     {
1693                         case XML_name:
1694                             if (get_config().debug)
1695                                 cout << "name='" << attr.value << "'; ";
1696                             break;
1697                         case XML_showRowHeaders:
1698                             b = to_bool(attr.value);
1699                             if (get_config().debug)
1700                                 cout << "show row headers=" << b << "; ";
1701                             break;
1702                         case XML_showColHeaders:
1703                             b = to_bool(attr.value);
1704                             if (get_config().debug)
1705                                 cout << "show column headers=" << b << "; ";
1706                         break;
1707                         case XML_showRowStripes:
1708                             b = to_bool(attr.value);
1709                             if (get_config().debug)
1710                                 cout << "show row stripes=" << b << "; ";
1711                         break;
1712                         case XML_showColStripes:
1713                             b = to_bool(attr.value);
1714                             if (get_config().debug)
1715                                 cout << "show column stripes=" << b << "; ";
1716                         break;
1717                         case XML_showLastColumn:
1718                             b = to_bool(attr.value);
1719                             if (get_config().debug)
1720                                 cout << "show last column=" << b << "; ";
1721                         break;
1722                         default:
1723                             ;
1724                     }
1725                 }
1726 
1727                 if (get_config().debug)
1728                     cout << endl;
1729                 break;
1730             }
1731             default:
1732                 warn_unhandled();
1733         }
1734     }
1735     else
1736         warn_unhandled();
1737 }
1738 
end_element(xmlns_id_t ns,xml_token_t name)1739 bool xlsx_pivot_table_context::end_element(xmlns_id_t ns, xml_token_t name)
1740 {
1741     return pop_stack(ns, name);
1742 }
1743 
characters(const pstring &,bool)1744 void xlsx_pivot_table_context::characters(const pstring& /*str*/, bool /*transient*/)
1745 {
1746 }
1747 
1748 }
1749 
1750 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1751