/*============================================================================= Copyright (c) 2011, 2013 Daniel James Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ #include "document_state_impl.hpp" #include "utils.hpp" #include #include #include #include namespace quickbook { struct file_info { boost::shared_ptr const parent; boost::shared_ptr const document; unsigned const compatibility_version; unsigned const depth; unsigned const override_depth; id_placeholder const* const override_id; // The 1.1-1.5 document id would actually change per file due to // explicit ids in includes and a bug which would sometimes use the // document title instead of the id. std::string const doc_id_1_1; // Constructor for files that aren't the root of a document. file_info(boost::shared_ptr const& parent, unsigned compatibility_version, quickbook::string_view doc_id_1_1, id_placeholder const* override_id) : parent(parent), document(parent->document), compatibility_version(compatibility_version), depth(parent->depth + 1), override_depth(override_id ? depth : parent->override_depth), override_id(override_id ? override_id : parent->override_id), doc_id_1_1(detail::to_s(doc_id_1_1)) {} // Constructor for files that are the root of a document. file_info(boost::shared_ptr const& parent, boost::shared_ptr const& document, unsigned compatibility_version, quickbook::string_view doc_id_1_1) : parent(parent), document(document), compatibility_version(compatibility_version), depth(0), override_depth(0), override_id(0), doc_id_1_1(detail::to_s(doc_id_1_1)) {} }; struct doc_info { boost::shared_ptr current_section; // Note: these are mutable to remain bug compatible with old versions // of quickbook. They would set these values at the start of new files // and sections and then not restore them at the end. std::string last_title_1_1; std::string section_id_1_1; }; struct section_info { boost::shared_ptr const parent; unsigned const compatibility_version; unsigned const file_depth; unsigned const level; value const explicit_id; std::string const id_1_1; id_placeholder const* const placeholder_1_6; source_mode_info const source_mode; section_info(boost::shared_ptr const& parent, file_info const* current_file, value const& explicit_id, quickbook::string_view id_1_1, id_placeholder const* placeholder_1_6, source_mode_info const& source_mode) : parent(parent), compatibility_version(current_file->compatibility_version), file_depth(current_file->depth), level(parent ? parent->level + 1 : 1), explicit_id(explicit_id), id_1_1(detail::to_s(id_1_1)), placeholder_1_6(placeholder_1_6), source_mode(source_mode) {} }; // // document_state // document_state::document_state() : state(new document_state_impl) { } document_state::~document_state() {} void document_state::start_file( unsigned compatibility_version, quickbook::string_view include_doc_id, quickbook::string_view id, value const& title) { state->start_file(compatibility_version, false, include_doc_id, id, title); } std::string document_state::start_file_with_docinfo( unsigned compatibility_version, quickbook::string_view include_doc_id, quickbook::string_view id, value const& title) { return state->start_file(compatibility_version, true, include_doc_id, id, title)->to_string(); } void document_state::end_file() { state->end_file(); } std::string document_state::begin_section( value const& explicit_id, quickbook::string_view id, id_category category, source_mode_info const& source_mode) { return state->begin_section(explicit_id, id, category, source_mode) ->to_string(); } void document_state::end_section() { return state->end_section(); } int document_state::section_level() const { return state->current_file->document->current_section->level; } value const& document_state::explicit_id() const { return state->current_file->document->current_section->explicit_id; } source_mode_info document_state::section_source_mode() const { return state->current_file ? state->current_file->document->current_section->source_mode : source_mode_info(); } std::string document_state::old_style_id(quickbook::string_view id, id_category category) { return state->old_style_id(id, category)->to_string(); } std::string document_state::add_id(quickbook::string_view id, id_category category) { return state->add_id(id, category)->to_string(); } std::string document_state::add_anchor(quickbook::string_view id, id_category category) { return state->add_placeholder(id, category)->to_string(); } std::string document_state::replace_placeholders_with_unresolved_ids( quickbook::string_view xml) const { return replace_ids(*state, xml); } std::string document_state::replace_placeholders(quickbook::string_view xml) const { assert(!state->current_file); std::vector ids = generate_ids(*state, xml); return replace_ids(*state, xml, &ids); } unsigned document_state::compatibility_version() const { return state->current_file->compatibility_version; } // // id_placeholder // id_placeholder::id_placeholder( unsigned index, quickbook::string_view id, id_category category, id_placeholder const* parent_) : index(index), unresolved_id(parent_ ? parent_->unresolved_id + '.' + detail::to_s(id) : detail::to_s(id)), id(id.begin(), id.end()), parent(parent_), category(category), num_dots(boost::range::count(id, '.') + (parent_ ? parent_->num_dots + 1 : 0)) { } std::string id_placeholder::to_string() const { return '$' + boost::lexical_cast(index); } // // document_state_impl // id_placeholder const* document_state_impl::add_placeholder( quickbook::string_view id, id_category category, id_placeholder const* parent) { placeholders.push_back(id_placeholder( placeholders.size(), id, category, parent)); return &placeholders.back(); } id_placeholder const* document_state_impl::get_placeholder(quickbook::string_view value) const { // If this isn't a placeholder id. if (value.size() <= 1 || *value.begin() != '$') return 0; unsigned index = boost::lexical_cast(std::string( value.begin() + 1, value.end())); return &placeholders.at(index); } id_placeholder const* document_state_impl::get_id_placeholder( boost::shared_ptr const& section) const { return !section ? 0 : section->file_depth < current_file->override_depth ? current_file->override_id : section->placeholder_1_6; } id_placeholder const* document_state_impl::start_file( unsigned compatibility_version, bool document_root, quickbook::string_view include_doc_id, quickbook::string_view id, value const& title) { boost::shared_ptr parent = current_file; assert(parent || document_root); boost::shared_ptr document = document_root ? boost::make_shared() : parent->document; // Choose specified id to use. Prefer 'include_doc_id' (the id // specified in an 'include' element) unless backwards compatibility // is required. quickbook::string_view initial_doc_id; if (document_root || compatibility_version >= 106u || parent->compatibility_version >= 106u) { initial_doc_id = !include_doc_id.empty() ? include_doc_id : id; } else { initial_doc_id = !id.empty() ? id : include_doc_id; } // Work out this file's doc_id for older versions of quickbook. // A bug meant that this need to be done per file, not per // document. std::string doc_id_1_1; if (document_root || compatibility_version < 106u) { if (title.check()) document->last_title_1_1 = detail::to_s(title.get_quickbook()); doc_id_1_1 = !initial_doc_id.empty() ? detail::to_s(initial_doc_id) : detail::make_identifier(document->last_title_1_1); } else if (parent) { doc_id_1_1 = parent->doc_id_1_1; } if (document_root) { // Create new file current_file = boost::make_shared(parent, document, compatibility_version, doc_id_1_1); // Create a section for the new document. source_mode_info default_source_mode; if (!initial_doc_id.empty()) { return create_new_section(empty_value(), id, id_category::explicit_section_id, default_source_mode); } else if (!title.empty()) { return create_new_section(empty_value(), detail::make_identifier(title.get_quickbook()), id_category::generated_doc, default_source_mode); } else if (compatibility_version >= 106u) { return create_new_section(empty_value(), "doc", id_category::numbered, default_source_mode); } else { return create_new_section(empty_value(), "", id_category::generated_doc, default_source_mode); } } else { // If an id was set for the file, then the file overrides the // current section's id with this id. // // Don't do this for document_root as it will create a section // for the document. // // Don't do this for older versions, as they use a different // backwards compatible mechanism to handle file ids. id_placeholder const* override_id = 0; if (!initial_doc_id.empty() && compatibility_version >= 106u) { boost::shared_ptr null_section; override_id = add_id_to_section(initial_doc_id, id_category::explicit_section_id, null_section); } // Create new file current_file = boost::make_shared(parent, compatibility_version, doc_id_1_1, override_id); return 0; } } void document_state_impl::end_file() { current_file = current_file->parent; } id_placeholder const* document_state_impl::add_id( quickbook::string_view id, id_category category) { return add_id_to_section(id, category, current_file->document->current_section); } id_placeholder const* document_state_impl::add_id_to_section( quickbook::string_view id, id_category category, boost::shared_ptr const& section) { std::string id_part(id.begin(), id.end()); // Note: Normalizing id according to file compatibility version, but // adding to section according to section compatibility version. if (current_file->compatibility_version >= 106u && category.c < id_category::explicit_id) { id_part = normalize_id(id); } id_placeholder const* placeholder_1_6 = get_id_placeholder(section); if(!section || section->compatibility_version >= 106u) { return add_placeholder(id_part, category, placeholder_1_6); } else { std::string const& qualified_id = section->id_1_1; std::string new_id; if (!placeholder_1_6) new_id = current_file->doc_id_1_1; if (!new_id.empty() && !qualified_id.empty()) new_id += '.'; new_id += qualified_id; if (!new_id.empty() && !id_part.empty()) new_id += '.'; new_id += id_part; return add_placeholder(new_id, category, placeholder_1_6); } } id_placeholder const* document_state_impl::old_style_id( quickbook::string_view id, id_category category) { return current_file->compatibility_version < 103u ? add_placeholder( current_file->document->section_id_1_1 + "." + detail::to_s(id), category) : add_id(id, category); } id_placeholder const* document_state_impl::begin_section( value const& explicit_id, quickbook::string_view id, id_category category, source_mode_info const& source_mode) { current_file->document->section_id_1_1 = detail::to_s(id); return create_new_section(explicit_id, id, category, source_mode); } id_placeholder const* document_state_impl::create_new_section( value const& explicit_id, quickbook::string_view id, id_category category, source_mode_info const& source_mode) { boost::shared_ptr parent = current_file->document->current_section; id_placeholder const* p = 0; id_placeholder const* placeholder_1_6 = 0; std::string id_1_1; if (parent && current_file->compatibility_version < 106u) { id_1_1 = parent->id_1_1; if (!id_1_1.empty() && !id.empty()) id_1_1 += "."; id_1_1.append(id.begin(), id.end()); } if (current_file->compatibility_version >= 106u) { p = placeholder_1_6 = add_id_to_section(id, category, parent); } else if (current_file->compatibility_version >= 103u) { placeholder_1_6 = get_id_placeholder(parent); std::string new_id; if (!placeholder_1_6) { new_id = current_file->doc_id_1_1; if (!id_1_1.empty()) new_id += '.'; } new_id += id_1_1; p = add_placeholder(new_id, category, placeholder_1_6); } else { placeholder_1_6 = get_id_placeholder(parent); std::string new_id; if (parent && !placeholder_1_6) new_id = current_file->doc_id_1_1 + '.'; new_id += detail::to_s(id); p = add_placeholder(new_id, category, placeholder_1_6); } current_file->document->current_section = boost::make_shared(parent, current_file.get(), explicit_id, id_1_1, placeholder_1_6, source_mode); return p; } void document_state_impl::end_section() { current_file->document->current_section = current_file->document->current_section->parent; } }