docs: protected void buildBookBase(final BuildData buildData) throws BuildProcessingException {
          final ContentSpec contentSpec = buildData.getContentSpec();
  
          // Get the template from the server
          final String bookXmlTemplate;
          if (contentSpec.getBookType() == BookType.ARTICLE || contentSpec.getBookType() == BookType.ARTICLE_DRAFT) {
              bookXmlTemplate = stringConstantProvider.getStringConstant(
                      buildData.getServerEntities().getArticleStringConstantId()).getValue();
          } else {
              bookXmlTemplate = stringConstantProvider.getStringConstant(buildData.getServerEntities().getBookStringConstantId()).getValue();
          }
  
          // Setup the basic book.xml
          String basicBook = bookXmlTemplate.replaceAll(BuilderConstants.ESCAPED_TITLE_REGEX, buildData.getEscapedBookTitle());
          basicBook = basicBook.replaceAll(BuilderConstants.PRODUCT_REGEX,
                  DocBookBuildUtilities.getKeyValueNodeText(buildData, contentSpec.getProductNode()));
          basicBook = basicBook.replaceAll(BuilderConstants.VERSION_REGEX,
                  DocBookBuildUtilities.getKeyValueNodeText(buildData, contentSpec.getVersionNode()));
          basicBook = basicBook.replaceAll(BuilderConstants.DRAFT_REGEX, buildData.getBuildOptions().getDraft() ? "status=\"draft\"" : "");
  
          if (contentSpec.getUseDefaultPreface()) {
              // Add the preface to the book.xml
              basicBook = basicBook.replaceAll(BuilderConstants.PREFACE_REGEX,
                      "<xi:include href=\"Preface.xml\" xmlns:xi=\"http://www.w3.org/2001/XInclude\" />");
          }
  
          // Remove the Injection sequence as we'll add the revision history and xiinclude element later
          basicBook = basicBook.replaceAll(BuilderConstants.XIINCLUDES_INJECTION_STRING, "");
          basicBook = basicBook.replaceAll(BuilderConstants.REV_HISTORY_REGEX, "");
  
          // Create the Book.xml DOM Document
          Document bookBase = null;
          try {
              // Find and Remove the Doctype first
              final String doctype = XMLUtilities.findDocumentType(basicBook);
  
              bookBase = XMLUtilities.convertStringToDocument(doctype == null ? basicBook : basicBook.replace(doctype, ""));
          } catch (Exception e) {
              throw new BuildProcessingException(e);
          }
  
          boolean flattenStructure = buildData.getBuildOptions().isServerBuild() || buildData.getBuildOptions().getFlatten();
          final List<org.jboss.pressgang.ccms.contentspec.Node> levelData = contentSpec.getBaseLevel().getChildNodes();
  
          // Loop through and create each chapter and the topics inside those chapters
          log.info("\tBuilding Level and Topic XML Files");
  
          for (final org.jboss.pressgang.ccms.contentspec.Node node : levelData) {
              // Check if the app should be shutdown
              if (isShuttingDown.get()) {
                  return;
              }
  
              if (node instanceof Level) {
                  final Level level = (Level) node;
  
                  if (level instanceof InitialContent && (level.hasSpecTopics() || level.hasCommonContents())) {
                      addLevelsInitialContent(buildData, (InitialContent) level, bookBase, bookBase.getDocumentElement(), false);
                  } else if (level.hasSpecTopics() || level.hasCommonContents()) {
                      // If the book is an article than just include it directly and don't create a new file
                      if (contentSpec.getBookType() == BookType.ARTICLE || contentSpec.getBookType() == BookType.ARTICLE_DRAFT) {
                          // Create the section and its title
                          final Element sectionNode = bookBase.createElement("section");
                          setUpRootElement(buildData, level, bookBase, sectionNode);
  
                          createContainerXML(buildData, level, bookBase, sectionNode, buildData.getBookTopicsFolder(), flattenStructure);
  
                          bookBase.getDocumentElement().appendChild(sectionNode);
                      } else {
                          final Element xiInclude = createRootContainerXML(buildData, bookBase, level, flattenStructure);
                          if (xiInclude != null) {
                              bookBase.getDocumentElement().appendChild(xiInclude);
                          }
                      }
                  } else if (buildData.getBuildOptions().isAllowEmptySections()) {
                      final Element para = bookBase.createElement("para");
                      para.setTextContent("No Content");
                      bookBase.getDocumentElement().appendChild(para);
                  }
              } else if (node instanceof SpecTopic) {
                  final SpecTopic specTopic = (SpecTopic) node;
                  final Node topicNode = createTopicDOMNode(specTopic, bookBase, flattenStructure, buildData.getBookTopicsFolder());
  
                  // Add the node to the Book
                  if (topicNode != null) {
                      bookBase.getDocumentElement().appendChild(topicNode);
                  }
              } else if (node instanceof CommonContent) {
                  final Node xiInclude = XMLUtilities.createXIInclude(bookBase, "Common_Content/" + ((CommonContent) node).getFixedTitle());
                  bookBase.getDocumentElement().appendChild(xiInclude);
              }
          }
  
          // Insert the editor link for the content spec if it's a translation
          if (buildData.getBuildOptions().getInsertEditorLinks() && buildData.isTranslationBuild()) {
              final String translateLinkChapter = buildTranslateCSChapter(buildData);
              buildData.getOutputFiles().put(buildData.getBookLocaleFolder() + "Translate.xml", StringUtilities.getStringBytes(
                      StringUtilities.cleanTextForXML(translateLinkChapter == null ? "" : translateLinkChapter)));
  
              // Create and append the XI Include element
              final Element translateXMLNode = XMLUtilities.createXIInclude(bookBase, "Translate.xml");
              bookBase.getDocumentElement().appendChild(translateXMLNode);
          }
  
          // Add any compiler errors
          if (!buildData.getBuildOptions().getSuppressErrorsPage() && buildData.getErrorDatabase().hasItems(buildData.getBuildLocale())) {
              final String compilerOutput = buildErrorChapter(buildData);
              buildData.getOutputFiles().put(buildData.getBookLocaleFolder() + "Errors.xml",
                      StringUtilities.getStringBytes(StringUtilities.cleanTextForXML(compilerOutput == null ? "" : compilerOutput)));
  
              // Create and append the XI Include element
              final Element translateXMLNode = XMLUtilities.createXIInclude(bookBase, "Errors.xml");
              bookBase.getDocumentElement().appendChild(translateXMLNode);
          }
  
          // Add the report chapter
          if (buildData.getBuildOptions().getShowReportPage()) {
              final String compilerOutput = buildReportChapter(buildData);
              buildData.getOutputFiles().put(buildData.getBookLocaleFolder() + "Report.xml",
                      StringUtilities.getStringBytes(StringUtilities.cleanTextForXML(compilerOutput == null ? "" : compilerOutput)));
  
              // Create and append the XI Include element
              final Element translateXMLNode = XMLUtilities.createXIInclude(bookBase, "Report.xml");
              bookBase.getDocumentElement().appendChild(translateXMLNode);
          }
  
          // Build the content specification page
          if (!buildData.getBuildOptions().getSuppressContentSpecPage()) {
              final String contentSpecPage = DocBookUtilities.buildAppendix(DocBookUtilities.wrapInPara(
                      "<programlisting>" + XMLUtilities.wrapStringInCDATA(contentSpec.toString(INCLUDE_CHECKSUMS)) + "</programlisting>"),
                      "Build Content Specification");
              addToZip(buildData.getBookLocaleFolder() + "Build_Content_Specification.xml",
                      DocBookBuildUtilities.addDocBookPreamble(buildData.getDocBookVersion(), contentSpecPage, "appendix",
                              buildData.getEntityFileName()), buildData);
  
              // Create and append the XI Include element
              final Element translateXMLNode = XMLUtilities.createXIInclude(bookBase, "Build_Content_Specification.xml");
              bookBase.getDocumentElement().appendChild(translateXMLNode);
          }
  
          // Add the revision history to the book.xml
          final Element revisionHistoryXMLNode = XMLUtilities.createXIInclude(bookBase, "Revision_History.xml");
          bookBase.getDocumentElement().appendChild(revisionHistoryXMLNode);
  
          // Add the index node if required
          if (contentSpec.getIncludeIndex()) {
              final Element indexNode = bookBase.createElement("index");
              bookBase.getDocumentElement().appendChild(indexNode);
          }
  
          // Change the DOM Document into a string so it can be added to the ZIP
          final String rootElementName = contentSpec.getBookType().toString().toLowerCase().replace("-draft", "");
          final String book = DocBookBuildUtilities.convertDocumentToDocBookFormattedString(buildData.getDocBookVersion(), bookBase,
                  rootElementName, buildData.getEntityFileName(), getXMLFormatProperties());
          addToZip(buildData.getBookLocaleFolder() + buildData.getRootBookFileName() + ".xml", book, buildData);
      }