docs: @SuppressWarnings("unchecked")
      private boolean validateTopicXML(final BuildData buildData, final ITopicNode topicNode,
              final Document topicDoc) throws BuildProcessingException {
          final XMLValidator validator = new XMLValidator();
          final ContentSpec contentSpec = buildData.getContentSpec();
          final BaseTopicWrapper<?> topic = topicNode.getTopic();
  
          final StringBuilder entity = new StringBuilder(CSConstants.DUMMY_CS_NAME_ENT_FILE);
          // Add any custom entities
          if (!isNullOrEmpty(contentSpec.getEntities())) {
              entity.append(contentSpec.getEntities());
          }
  
          // Wrap the document so it can be validated.
          final String xml = XMLUtilities.convertDocumentToString(topicDoc, ENCODING);
          final Pair<String, String> wrappedTopic = DocBookUtilities.wrapForValidation(buildData.getDocBookVersion(), xml);
          final String fixedTopicXml = wrappedTopic.getSecond();
          final String rootElementName = wrappedTopic.getFirst();
  
          // Get the schema/dtd as well as any additional content required for validation
          final String titleXML;
          final String docbookFileName;
          final XMLValidator.ValidationMethod validationMethod;
          final byte[] docbookSchema;
          if (buildData.getDocBookVersion() == DocBookVersion.DOCBOOK_50) {
              docbookFileName = BuilderConstants.DOCBOOK_50_RNG;
              validationMethod = XMLValidator.ValidationMethod.RELAXNG;
              docbookSchema = docbookRng.getValue();
              titleXML = DocBookUtilities.addDocBook50Namespace("<section><title>" + topic.getTitle() + "</title><para /></section>",
                      "section");
              entity.append(docbook45Entities);
          } else {
              docbookFileName = BuilderConstants.ROCBOOK_45_DTD;
              validationMethod = XMLValidator.ValidationMethod.DTD;
              docbookSchema = rocbookDtd.getValue();
              titleXML = "<section><title>" + topic.getTitle() + "</title><para /></section>";
          }
          final String entityData = entity.toString();
  
          // First check to see if the title is valid XML
          if (!validator.validate(validationMethod, titleXML, docbookFileName, docbookSchema, entityData, "section")) {
              // The title is invalid so replace it with something that is valid
              topic.setTitle("Invalid Topic");
              DocBookUtilities.setSectionTitle(buildData.getDocBookVersion(), topic.getTitle(), topicDoc);
          }
  
          // Validate the topic against its DTD/Schema
          if (!validator.validate(validationMethod, fixedTopicXml, docbookFileName, docbookSchema, entityData, rootElementName)) {
              // Store the error message
              final String errorMsg = validator.getErrorText();
              final String cleanedErrorMsg = errorMsg.replace("|", " | ").replace(",", ", ").replaceFirst("\\.$", "");
  
              final String xmlStringInCDATA = DocBookBuildUtilities.convertDocumentToCDATAFormattedString(topicDoc, getXMLFormatProperties());
              buildData.getErrorDatabase().addError(topic, ErrorType.INVALID_CONTENT,
                      BuilderConstants.ERROR_INVALID_TOPIC_XML + " The error is <emphasis>" + StringEscapeUtils.escapeXml(
                              cleanedErrorMsg) + "</emphasis>. The processed XML is <programlisting>" + xmlStringInCDATA +
                              "</programlisting>");
              DocBookBuildUtilities.setTopicNodeXMLForError(buildData, topicNode, getErrorInvalidValidationTopicTemplate().getValue());
  
              return false;
          }
  
          // Check the content of the XML for things not picked up by DTD validation
          final List<String> xmlErrors = DocBookBuildUtilities.checkTopicForInvalidContent(topicNode, topic, topicDoc, buildData);
          if (xmlErrors.size() > 0) {
              final String xmlStringInCDATA = DocBookBuildUtilities.convertDocumentToCDATAFormattedString(topicDoc, getXMLFormatProperties());
  
              // Escape the XML Errors as they will currently be in plain text
              final List<String> escapedXmlErrors = new ArrayList<String>();
              for (final String xmlError : xmlErrors) {
                  escapedXmlErrors.add(StringEscapeUtils.escapeXml(xmlError));
              }
  
              // Add the error and processed XML to the error message
              final String errorMessage;
              if (escapedXmlErrors.size() > 1) {
                  errorMessage = "<itemizedlist><listitem><para>" + CollectionUtilities.toSeperatedString(escapedXmlErrors,
                      "</para></listitem><listitem><para>") + "</para></listitem></itemizedlist>";
              } else {
                  errorMessage = escapedXmlErrors.get(0);
              }
              buildData.getErrorDatabase().addError(topic, ErrorType.INVALID_CONTENT,
                      BuilderConstants.ERROR_INVALID_TOPIC_XML + " " + errorMessage + "</para><para>The processed XML is <programlisting>" +
                              xmlStringInCDATA + "</programlisting>");
  
              DocBookBuildUtilities.setTopicNodeXMLForError(buildData, topicNode, getErrorInvalidValidationTopicTemplate().getValue());
  
              return false;
          }
  
          return true;
      }