1 /*=============================================================================
2 Copyright (c) 2017 Daniel James
3 
4 Use, modification and distribution is subject to the Boost Software
5 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
6 http://www.boost.org/LICENSE_1_0.txt)
7 =============================================================================*/
8 
9 #include "boostbook_chunker.hpp"
10 #include <boost/algorithm/string/replace.hpp>
11 #include <boost/lexical_cast.hpp>
12 #include <boost/unordered_set.hpp>
13 
14 namespace quickbook
15 {
16     namespace detail
17     {
18         boost::unordered_set<std::string> chunk_types;
19         boost::unordered_set<std::string> chunkinfo_types;
20 
21         static struct init_chunk_type
22         {
init_chunk_typequickbook::detail::init_chunk_type23             init_chunk_type()
24             {
25                 chunk_types.insert("book");
26                 chunk_types.insert("article");
27                 chunk_types.insert("library");
28                 chunk_types.insert("chapter");
29                 chunk_types.insert("part");
30                 chunk_types.insert("appendix");
31                 chunk_types.insert("preface");
32                 chunk_types.insert("qandadiv");
33                 chunk_types.insert("qandaset");
34                 chunk_types.insert("reference");
35                 chunk_types.insert("set");
36                 chunk_types.insert("section");
37 
38                 for (boost::unordered_set<std::string>::const_iterator it =
39                          chunk_types.begin();
40                      it != chunk_types.end(); ++it) {
41                     chunkinfo_types.insert(*it + "info");
42                 }
43             }
44         } init_chunk;
45 
46         struct chunk_builder : tree_builder<chunk>
47         {
48             int count;
49 
chunk_builderquickbook::detail::chunk_builder50             chunk_builder() : count(0) {}
51 
next_path_namequickbook::detail::chunk_builder52             std::string next_path_name()
53             {
54                 ++count;
55                 std::string result = "page-";
56                 result += boost::lexical_cast<std::string>(count);
57                 ++count;
58                 return result;
59             }
60         };
61 
62         void chunk_nodes(
63             chunk_builder& builder, xml_tree& tree, xml_element* node);
64         std::string id_to_path(quickbook::string_view);
65         void inline_chunks(chunk*);
66 
chunk_document(xml_tree & tree)67         chunk_tree chunk_document(xml_tree& tree)
68         {
69             chunk_builder builder;
70             for (xml_element* it = tree.root(); it;) {
71                 xml_element* next = it->next();
72                 chunk_nodes(builder, tree, it);
73                 it = next;
74             }
75 
76             return builder.release();
77         }
78 
inline_sections(chunk * c,int depth)79         void inline_sections(chunk* c, int depth)
80         {
81             if (c->contents_.root()->name_ == "section" && depth > 1) {
82                 --depth;
83             }
84 
85             // When depth is 0, inline leading sections.
86             chunk* it = c->children();
87             if (depth == 0) {
88                 for (; it && it->contents_.root()->name_ == "section";
89                      it = it->next()) {
90                     inline_chunks(it);
91                 }
92             }
93 
94             for (; it; it = it->next()) {
95                 inline_sections(it, depth);
96             }
97         }
98 
inline_all(chunk * c)99         void inline_all(chunk* c)
100         {
101             for (chunk* it = c->children(); it; it = it->next()) {
102                 inline_chunks(it);
103             }
104         }
105 
inline_chunks(chunk * c)106         void inline_chunks(chunk* c)
107         {
108             c->inline_ = true;
109             c->path_ = c->parent()->path_;
110             for (chunk* it = c->children(); it; it = it->next()) {
111                 inline_chunks(it);
112             }
113         }
114 
chunk_nodes(chunk_builder & builder,xml_tree & tree,xml_element * node)115         void chunk_nodes(
116             chunk_builder& builder, xml_tree& tree, xml_element* node)
117         {
118             chunk* parent = builder.parent();
119 
120             if (parent && node->type_ == xml_element::element_node &&
121                 node->name_ == "title") {
122                 parent->title_ = tree.extract(node);
123             }
124             else if (
125                 parent && node->type_ == xml_element::element_node &&
126                 chunkinfo_types.find(node->name_) != chunkinfo_types.end()) {
127                 parent->info_ = tree.extract(node);
128             }
129             else if (
130                 node->type_ == xml_element::element_node &&
131                 chunk_types.find(node->name_) != chunk_types.end()) {
132                 chunk* chunk_node = new chunk(tree.extract(node));
133                 builder.add_element(chunk_node);
134 
135                 chunk_node->id_ = node->has_attribute("id")
136                                       ? node->get_attribute("id").to_s()
137                                       : builder.next_path_name();
138                 chunk_node->path_ = id_to_path(chunk_node->id_);
139 
140                 builder.start_children();
141                 for (xml_element* it = node->children(); it;) {
142                     xml_element* next = it->next();
143                     chunk_nodes(builder, tree, it);
144                     it = next;
145                 }
146                 builder.end_children();
147             }
148         }
149 
id_to_path(quickbook::string_view id)150         std::string id_to_path(quickbook::string_view id)
151         {
152             std::string result(id.begin(), id.end());
153             boost::replace_all(result, ".", "/");
154             result += ".html";
155             return result;
156         }
157     }
158 }
159