docs: private void doTopicPass(final BuildData buildData, final Map<String, BaseTopicWrapper<?>> topics) throws BuildProcessingException {
          log.info("Doing " + buildData.getBuildLocale() + " First topic pass");
  
          // Check that we have some topics to process
          if (topics != null) {
              log.info("\tProcessing " + topics.size() + " Topics");
  
              final int showPercent = 10;
              final float total = topics.size();
              float current = 0;
              int lastPercent = 0;
  
              // Process each topic
              for (final Entry<String, BaseTopicWrapper<?>> topicEntry : topics.entrySet()) {
                  final BaseTopicWrapper<?> topic = topicEntry.getValue();
                  final String key = topicEntry.getKey();
  
                  ++current;
                  final int percent = Math.round(current / total * 100);
                  if (percent - lastPercent >= showPercent) {
                      lastPercent = percent;
                      log.info("\tFirst topic Pass " + percent + "% Done");
                  }
  
                  // Get the Topic ID
                  final Integer topicRevision = topic.getTopicRevision();
  
                  boolean revHistoryTopic = topic.hasTag(buildData.getServerEntities().getRevisionHistoryTagId());
                  boolean legalNoticeTopic = topic.hasTag(buildData.getServerEntities().getLegalNoticeTagId());
                  boolean authorGroupTopic = topic.hasTag(buildData.getServerEntities().getAuthorGroupTagId());
                  boolean abstractTopic = topic.hasTag(buildData.getServerEntities().getAbstractTagId());
                  boolean infoTopic = topic.hasTag(buildData.getServerEntities().getInfoTagId());
  
                  Document topicDoc = null;
                  final String topicXML = topic.getXml();
  
                  // Check if the app should be shutdown
                  if (isShuttingDown.get()) {
                      return;
                  }
  
                  boolean xmlValid = true;
  
                  // Check that the Topic XML exists and isn't empty
                  if (topicXML == null || topicXML.trim().isEmpty()) {
                      buildData.getErrorDatabase().addWarning(topic, ErrorType.NO_CONTENT, BuilderConstants.WARNING_EMPTY_TOPIC_XML);
                      topicDoc = DocBookBuildUtilities.setTopicXMLForError(buildData, topic, getErrorEmptyTopicTemplate().getValue());
                      xmlValid = false;
                  }
  
                  // Check if the app should be shutdown
                  if (isShuttingDown.get()) {
                      return;
                  }
  
                  // Make sure we have valid XML
                  if (xmlValid) {
                      try {
                          final String fixedTopicXML;
                          if (buildData.getDocBookVersion() == DocBookVersion.DOCBOOK_50) {
                              fixedTopicXML = DocBookUtilities.addDocBook50Namespace(topicXML);
                          } else {
                              fixedTopicXML = topicXML;
                          }
  
                          topicDoc = XMLUtilities.convertStringToDocument(fixedTopicXML);
  
                          if (topicDoc == null) {
                              final String xmlStringInCDATA = XMLUtilities.wrapStringInCDATA(topic.getXml());
                              buildData.getErrorDatabase().addError(topic, ErrorType.INVALID_CONTENT,
                                      BuilderConstants.ERROR_INVALID_XML_CONTENT + " The processed XML is <programlisting>" +
                                              xmlStringInCDATA + "</programlisting>");
                              topicDoc = DocBookBuildUtilities.setTopicXMLForError(buildData, topic,
                                      getErrorInvalidValidationTopicTemplate().getValue());
                          }
                      } catch (Exception ex) {
                          final String xmlStringInCDATA = XMLUtilities.wrapStringInCDATA(topic.getXml());
                          buildData.getErrorDatabase().addError(topic, ErrorType.INVALID_CONTENT,
                                  BuilderConstants.ERROR_BAD_XML_STRUCTURE + " " + StringUtilities.escapeForXML(
                                          ex.getMessage()) + " The processed XML is <programlisting>" + xmlStringInCDATA +
                                          "</programlisting>");
                          topicDoc = DocBookBuildUtilities.setTopicXMLForError(buildData, topic,
                                  getErrorInvalidValidationTopicTemplate().getValue());
                      }
                  }
  
                  // Make sure the topic has the correct root element and other items
                  if (revHistoryTopic) {
                      // If it is a translated build then check if we have anything more to merge together
                      if (buildData.isTranslationBuild()) {
                          topicDoc = mergeAdditionalTranslatedXML(buildData, topicDoc, (TranslatedTopicWrapper) topic,
                                  TopicType.REVISION_HISTORY);
                      }
  
                      DocBookUtilities.wrapDocumentInAppendix(topicDoc);
                  } else if (authorGroupTopic) {
                      // If it is a translated build then check if we have anything more to merge together
                      if (buildData.isTranslationBuild()) {
                          topicDoc = mergeAdditionalTranslatedXML(buildData, topicDoc, (TranslatedTopicWrapper) topic,
                                  TopicType.AUTHOR_GROUP);
                      }
  
                      DocBookUtilities.wrapDocumentInAuthorGroup(topicDoc);
                  } else if (legalNoticeTopic) {
                      DocBookUtilities.wrapDocumentInLegalNotice(topicDoc);
                  } else if (abstractTopic) {
                      DocBookUtilities.wrapDocument(topicDoc, "abstract");
                  } else if (infoTopic) {
                      if (buildData.getDocBookVersion() == DocBookVersion.DOCBOOK_50) {
                          DocBookUtilities.wrapDocument(topicDoc, "info");
                      } else {
                          DocBookUtilities.wrapDocument(topicDoc, "sectioninfo");
                      }
                  } else {
                      // Ensure the topic is wrapped in a section and the title matches the topic
                      DocBookUtilities.wrapDocumentInSection(topicDoc);
                      DocBookUtilities.setSectionTitle(buildData.getDocBookVersion(), topic.getTitle(), topicDoc);
  
                      processTopicSectionInfo(buildData, topic, topicDoc);
                  }
  
                  // Add the document & topic to the database spec topics
                  final List<ITopicNode> specTopics = buildData.getBuildDatabase().getTopicNodesForKey(key);
                  for (final ITopicNode specTopic : specTopics) {
                      // Check if the app should be shutdown
                      if (isShuttingDown.get()) {
                          return;
                      }
  
                      if (buildData.getBuildOptions().getUseLatestVersions()) {
                          specTopic.setTopic(topic.clone(false));
                          specTopic.setXMLDocument((Document) topicDoc.cloneNode(true));
                      } else {
                          /*
                           * Only set the topic for the spec topic if it matches the spec topic revision. If the Spec Topic
                           * Revision is null then we need to ensure that we get the latest version of the topic that was
                           * downloaded.
                           */
                          if ((specTopic.getRevision() == null && (specTopic.getTopic() == null || specTopic.getTopic().getRevision() >=
                                  topicRevision)) || (specTopic.getRevision() != null && specTopic.getRevision() >= topicRevision)) {
                              specTopic.setTopic(topic.clone(false));
                              specTopic.setXMLDocument((Document) topicDoc.cloneNode(true));
                          }
                      }
                  }
  
              }
          } else {
              log.info("\tProcessing 0 Topics");
          }
      }