1 /*=============================================================================
2 Copyright (c) 2011, 2013 Daniel James
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 =============================================================================*/
9 #include "document_state_impl.hpp"
11 #include <boost/make_shared.hpp>
12 #include <boost/lexical_cast.hpp>
13 #include <boost/range/algorithm.hpp>
20 boost::shared_ptr
<file_info
> const parent
;
21 boost::shared_ptr
<doc_info
> const document
;
23 unsigned const compatibility_version
;
25 unsigned const override_depth
;
26 id_placeholder
const* const override_id
;
28 // The 1.1-1.5 document id would actually change per file due to
29 // explicit ids in includes and a bug which would sometimes use the
30 // document title instead of the id.
31 std::string
const doc_id_1_1
;
33 // Constructor for files that aren't the root of a document.
34 explicit file_info(boost::shared_ptr
<file_info
> const& parent_
,
35 unsigned compatibility_version_
,
36 quickbook::string_view doc_id_1_1_
,
37 id_placeholder
const* override_id_
) :
38 parent(parent_
), document(parent
->document
),
39 compatibility_version(compatibility_version_
),
40 depth(parent
->depth
+ 1),
41 override_depth(override_id_
? depth
: parent
->override_depth
),
42 override_id(override_id_
? override_id_
: parent
->override_id
),
43 doc_id_1_1(doc_id_1_1_
.to_s())
46 // Constructor for files that are the root of a document.
47 explicit file_info(boost::shared_ptr
<file_info
> const& parent_
,
48 boost::shared_ptr
<doc_info
> const& document_
,
49 unsigned compatibility_version_
,
50 quickbook::string_view doc_id_1_1_
) :
51 parent(parent_
), document(document_
),
52 compatibility_version(compatibility_version_
),
53 depth(0), override_depth(0), override_id(0),
54 doc_id_1_1(doc_id_1_1_
.to_s())
60 boost::shared_ptr
<section_info
> current_section
;
62 // Note: these are mutable to remain bug compatible with old versions
63 // of quickbook. They would set these values at the start of new files
64 // and sections and then not restore them at the end.
65 std::string last_title_1_1
;
66 std::string section_id_1_1
;
71 boost::shared_ptr
<section_info
> const parent
;
72 unsigned const compatibility_version
;
73 unsigned const file_depth
;
76 value
const explicit_id
;
77 std::string
const id_1_1
;
78 id_placeholder
const* const placeholder_1_6
;
79 source_mode_info
const source_mode
;
81 explicit section_info(boost::shared_ptr
<section_info
> const& parent_
,
82 file_info
const* current_file_
,
83 value
const& explicit_id_
,
84 quickbook::string_view id_1_1_
,
85 id_placeholder
const* placeholder_1_6_
,
86 source_mode_info
const& source_mode_
) :
88 compatibility_version(current_file_
->compatibility_version
),
89 file_depth(current_file_
->depth
),
90 level(parent
? parent
->level
+ 1 : 1),
91 explicit_id(explicit_id_
),
92 id_1_1(id_1_1_
.to_s()),
93 placeholder_1_6(placeholder_1_6_
),
94 source_mode(source_mode_
) {}
101 document_state::document_state()
102 : state(new document_state_impl
)
106 document_state::~document_state() {}
108 void document_state::start_file(
109 unsigned compatibility_version_
,
110 quickbook::string_view include_doc_id
,
111 quickbook::string_view id
,
114 state
->start_file(compatibility_version_
, false, include_doc_id
, id
, title_
);
117 std::string
document_state::start_file_with_docinfo(
118 unsigned compatibility_version_
,
119 quickbook::string_view include_doc_id
,
120 quickbook::string_view id
,
123 return state
->start_file(compatibility_version_
, true, include_doc_id
,
124 id
, title_
)->to_string();
127 void document_state::end_file()
132 std::string
document_state::begin_section(
133 value
const& explicit_id_
, quickbook::string_view id
,
134 id_category category
, source_mode_info
const& source_mode
)
136 return state
->begin_section(explicit_id_
, id
, category
, source_mode
)
140 void document_state::end_section()
142 return state
->end_section();
145 int document_state::section_level() const
147 return state
->current_file
->document
->current_section
->level
;
150 value
const& document_state::explicit_id() const
152 return state
->current_file
->document
->current_section
->explicit_id
;
155 source_mode_info
document_state::section_source_mode() const
157 return state
->current_file
?
158 state
->current_file
->document
->current_section
->source_mode
:
162 std::string
document_state::old_style_id(quickbook::string_view id
, id_category category
)
164 return state
->old_style_id(id
, category
)->to_string();
167 std::string
document_state::add_id(quickbook::string_view id
, id_category category
)
169 return state
->add_id(id
, category
)->to_string();
172 std::string
document_state::add_anchor(quickbook::string_view id
, id_category category
)
174 return state
->add_placeholder(id
, category
)->to_string();
177 std::string
document_state::replace_placeholders_with_unresolved_ids(
178 quickbook::string_view xml
) const
180 return replace_ids(*state
, xml
);
183 std::string
document_state::replace_placeholders(quickbook::string_view xml
) const
185 assert(!state
->current_file
);
186 std::vector
<std::string
> ids
= generate_ids(*state
, xml
);
187 return replace_ids(*state
, xml
, &ids
);
190 unsigned document_state::compatibility_version() const
192 return state
->current_file
->compatibility_version
;
199 id_placeholder::id_placeholder(
201 quickbook::string_view id_
,
202 id_category category_
,
203 id_placeholder
const* parent_
)
205 id(id_
.begin(), id_
.end()),
206 unresolved_id(parent_
? parent_
->unresolved_id
+ '.' + id
: id
),
209 num_dots(boost::range::count(id
, '.') +
210 (parent_
? parent_
->num_dots
+ 1 : 0))
214 std::string
id_placeholder::to_string() const
216 return '$' + boost::lexical_cast
<std::string
>(index
);
220 // document_state_impl
223 id_placeholder
const* document_state_impl::add_placeholder(
224 quickbook::string_view id
, id_category category
,
225 id_placeholder
const* parent
)
227 placeholders
.push_back(id_placeholder(
228 placeholders
.size(), id
, category
, parent
));
229 return &placeholders
.back();
232 id_placeholder
const* document_state_impl::get_placeholder(quickbook::string_view value
) const
234 // If this isn't a placeholder id.
235 if (value
.size() <= 1 || *value
.begin() != '$')
238 unsigned index
= boost::lexical_cast
<unsigned>(std::string(
239 value
.begin() + 1, value
.end()));
241 return &placeholders
.at(index
);
244 id_placeholder
const* document_state_impl::get_id_placeholder(
245 boost::shared_ptr
<section_info
> const& section
) const
247 return !section
? 0 :
248 section
->file_depth
< current_file
->override_depth
?
249 current_file
->override_id
: section
->placeholder_1_6
;
252 id_placeholder
const* document_state_impl::start_file(
253 unsigned compatibility_version
,
255 quickbook::string_view include_doc_id
,
256 quickbook::string_view id
,
259 boost::shared_ptr
<file_info
> parent
= current_file
;
260 assert(parent
|| document_root
);
262 boost::shared_ptr
<doc_info
> document
=
263 document_root
? boost::make_shared
<doc_info
>() : parent
->document
;
265 // Choose specified id to use. Prefer 'include_doc_id' (the id
266 // specified in an 'include' element) unless backwards compatibility
269 quickbook::string_view initial_doc_id
;
272 compatibility_version
>= 106u ||
273 parent
->compatibility_version
>= 106u)
275 initial_doc_id
= !include_doc_id
.empty() ? include_doc_id
: id
;
278 initial_doc_id
= !id
.empty() ? id
: include_doc_id
;
281 // Work out this file's doc_id for older versions of quickbook.
282 // A bug meant that this need to be done per file, not per
285 std::string doc_id_1_1
;
287 if (document_root
|| compatibility_version
< 106u) {
289 document
->last_title_1_1
= title
.get_quickbook().to_s();
291 doc_id_1_1
= !initial_doc_id
.empty() ? initial_doc_id
.to_s() :
292 detail::make_identifier(document
->last_title_1_1
);
295 doc_id_1_1
= parent
->doc_id_1_1
;
301 current_file
= boost::make_shared
<file_info
>(parent
,
302 document
, compatibility_version
, doc_id_1_1
);
304 // Create a section for the new document.
306 source_mode_info default_source_mode
;
308 if (!initial_doc_id
.empty()) {
309 return create_new_section(empty_value(), id
,
310 id_category::explicit_section_id
, default_source_mode
);
312 else if (!title
.empty()) {
313 return create_new_section(empty_value(),
314 detail::make_identifier(title
.get_quickbook()),
315 id_category::generated_doc
, default_source_mode
);
317 else if (compatibility_version
>= 106u) {
318 return create_new_section(empty_value(), "doc",
319 id_category::numbered
, default_source_mode
);
322 return create_new_section(empty_value(), "",
323 id_category::generated_doc
, default_source_mode
);
327 // If an id was set for the file, then the file overrides the
328 // current section's id with this id.
330 // Don't do this for document_root as it will create a section
333 // Don't do this for older versions, as they use a different
334 // backwards compatible mechanism to handle file ids.
336 id_placeholder
const* override_id
= 0;
338 if (!initial_doc_id
.empty() && compatibility_version
>= 106u)
340 boost::shared_ptr
<section_info
> null_section
;
342 override_id
= add_id_to_section(initial_doc_id
,
343 id_category::explicit_section_id
, null_section
);
349 boost::make_shared
<file_info
>(parent
, compatibility_version
,
350 doc_id_1_1
, override_id
);
356 void document_state_impl::end_file()
358 current_file
= current_file
->parent
;
361 id_placeholder
const* document_state_impl::add_id(
362 quickbook::string_view id
,
363 id_category category
)
365 return add_id_to_section(id
, category
,
366 current_file
->document
->current_section
);
369 id_placeholder
const* document_state_impl::add_id_to_section(
370 quickbook::string_view id
,
371 id_category category
,
372 boost::shared_ptr
<section_info
> const& section
)
374 std::string
id_part(id
.begin(), id
.end());
376 // Note: Normalizing id according to file compatibility version, but
377 // adding to section according to section compatibility version.
379 if (current_file
->compatibility_version
>= 106u &&
380 category
.c
< id_category::explicit_id
) {
381 id_part
= normalize_id(id
);
384 id_placeholder
const* placeholder_1_6
= get_id_placeholder(section
);
386 if(!section
|| section
->compatibility_version
>= 106u) {
387 return add_placeholder(id_part
, category
, placeholder_1_6
);
390 std::string
const& qualified_id
= section
->id_1_1
;
393 if (!placeholder_1_6
)
394 new_id
= current_file
->doc_id_1_1
;
395 if (!new_id
.empty() && !qualified_id
.empty()) new_id
+= '.';
396 new_id
+= qualified_id
;
397 if (!new_id
.empty() && !id_part
.empty()) new_id
+= '.';
400 return add_placeholder(new_id
, category
, placeholder_1_6
);
404 id_placeholder
const* document_state_impl::old_style_id(
405 quickbook::string_view id
,
406 id_category category
)
408 return current_file
->compatibility_version
< 103u ?
410 current_file
->document
->section_id_1_1
+ "." + id
.to_s(), category
) :
411 add_id(id
, category
);
414 id_placeholder
const* document_state_impl::begin_section(
415 value
const& explicit_id
,
416 quickbook::string_view id
,
417 id_category category
,
418 source_mode_info
const& source_mode
)
420 current_file
->document
->section_id_1_1
= id
.to_s();
421 return create_new_section(explicit_id
, id
, category
, source_mode
);
424 id_placeholder
const* document_state_impl::create_new_section(
425 value
const& explicit_id
,
426 quickbook::string_view id
,
427 id_category category
,
428 source_mode_info
const& source_mode
)
430 boost::shared_ptr
<section_info
> parent
=
431 current_file
->document
->current_section
;
433 id_placeholder
const* p
= 0;
434 id_placeholder
const* placeholder_1_6
= 0;
438 if (parent
&& current_file
->compatibility_version
< 106u) {
439 id_1_1
= parent
->id_1_1
;
440 if (!id_1_1
.empty() && !id
.empty())
442 id_1_1
.append(id
.begin(), id
.end());
445 if (current_file
->compatibility_version
>= 106u) {
446 p
= placeholder_1_6
= add_id_to_section(id
, category
, parent
);
448 else if (current_file
->compatibility_version
>= 103u) {
449 placeholder_1_6
= get_id_placeholder(parent
);
452 if (!placeholder_1_6
) {
453 new_id
= current_file
->doc_id_1_1
;
454 if (!id_1_1
.empty()) new_id
+= '.';
458 p
= add_placeholder(new_id
, category
, placeholder_1_6
);
461 placeholder_1_6
= get_id_placeholder(parent
);
464 if (parent
&& !placeholder_1_6
)
465 new_id
= current_file
->doc_id_1_1
+ '.';
469 p
= add_placeholder(new_id
, category
, placeholder_1_6
);
472 current_file
->document
->current_section
=
473 boost::make_shared
<section_info
>(parent
,
474 current_file
.get(), explicit_id
, id_1_1
, placeholder_1_6
,
480 void document_state_impl::end_section()
482 current_file
->document
->current_section
=
483 current_file
->document
->current_section
->parent
;