docs: protected void mergeAndSaveContentSpec(final DataProviderFactory providerFactory, final ProcessorData processorData,
              boolean create) throws Exception {
          // Get the providers
          final ContentSpecProvider contentSpecProvider = providerFactory.getProvider(ContentSpecProvider.class);
          final PropertyTagProvider propertyTagProvider = providerFactory.getProvider(PropertyTagProvider.class);
          final TagProvider tagProvider = providerFactory.getProvider(TagProvider.class);
  
          // Create the temporary entity to store changes in and load the real entity if it exists.
          final ContentSpec contentSpec = processorData.getContentSpec();
          ContentSpecWrapper contentSpecEntity = null;
          if (contentSpec.getId() != null) {
              contentSpecEntity = contentSpecProvider.getContentSpec(contentSpec.getId());
          } else if (create) {
              contentSpecEntity = contentSpecProvider.newContentSpec();
  
              // setup the basic values
              final LocaleWrapper locale = contentSpec.getLocale() != null ?
                      EntityUtilities.findLocaleFromString(serverSettings.getLocales(), contentSpec.getLocale())
                      : serverSettings.getDefaultLocale();
              contentSpecEntity.setLocale(locale);
  
              if (processorData.getUsername() != null) {
                  // Add the added by property tag
                  final UpdateableCollectionWrapper<PropertyTagInContentSpecWrapper> propertyTagCollection = propertyTagProvider
                          .newPropertyTagInContentSpecCollection(contentSpecEntity);
  
                  // Create the new property tag
                  final PropertyTagWrapper addedByProperty = propertyTagProvider.getPropertyTag(serverEntities.getAddedByPropertyTagId());
                  final PropertyTagInContentSpecWrapper propertyTag = propertyTagProvider.newPropertyTagInContentSpec(addedByProperty,
                          contentSpecEntity);
                  propertyTag.setValue(processorData.getUsername());
                  propertyTagCollection.addNewItem(propertyTag);
  
                  // Set the updated properties for the content spec
                  contentSpecEntity.setProperties(propertyTagCollection);
              }
          } else {
              throw new ProcessingException("Unable to find the existing Content Specification");
          }
  
          // Check that the type still matches
          final int typeId = BookType.getBookTypeId(contentSpec.getBookType());
          if (contentSpecEntity.getType() == null || !contentSpecEntity.getType().equals(typeId)) {
              contentSpecEntity.setType(typeId);
          }
  
          final ArrayList<CSNodeWrapper> contentSpecNodes = new ArrayList<CSNodeWrapper>();
          if (contentSpecEntity.getChildren() != null) {
              contentSpecNodes.addAll(contentSpecEntity.getChildren().getItems());
          }
  
          // Create the content spec entity so that we have a valid reference to add nodes to
          if (create) {
              contentSpecEntity = contentSpecProvider.createContentSpec(contentSpecEntity);
          }
  
          // Check that the content spec was updated/created successfully.
          if (contentSpecEntity == null) {
              throw new ProcessingException("Saving the updated Content Specification failed.");
          }
          contentSpec.setId(contentSpecEntity.getId());
  
          // Set the bug links last validated property
          if (processorData.isBugLinksReValidated()) {
              // Get the collection to use
              final UpdateableCollectionWrapper<PropertyTagInContentSpecWrapper> propertyTagCollection = contentSpecEntity.getProperties()
                      == null ? propertyTagProvider.newPropertyTagInContentSpecCollection(
                      contentSpecEntity) : contentSpecEntity.getProperties();
  
              // Check if the property already exists and if so remove it, and then create a new one to ensure it a revision is created
              for (final PropertyTagInContentSpecWrapper propertyTag : propertyTagCollection.getItems()) {
                  if (propertyTag.getId().equals(serverEntities.getBugLinksLastValidatedPropertyTagId())) {
                      propertyTag.setValue(Long.toString(new Date().getTime()));
                      propertyTagCollection.remove(propertyTag);
                      propertyTagCollection.addRemoveItem(propertyTag);
                  }
              }
  
              // Add the new tag
              final PropertyTagWrapper lastUpdatedProperty = propertyTagProvider.getPropertyTag(
                      serverEntities.getBugLinksLastValidatedPropertyTagId());
              final PropertyTagInContentSpecWrapper propertyTag = propertyTagProvider.newPropertyTagInContentSpec(lastUpdatedProperty,
                      contentSpecEntity);
              propertyTag.setValue(Long.toString(new Date().getTime()));
              propertyTagCollection.addNewItem(propertyTag);
  
              // Set the updated properties for the content spec
              contentSpecEntity.setProperties(propertyTagCollection);
          }
  
          // Add any global book tags
          mergeGlobalOptions(contentSpecEntity, contentSpec, tagProvider);
  
          // Get the list of transformable child nodes for processing
          final List<Node> nodes = getTransformableNodes(contentSpec.getNodes());
          nodes.addAll(getTransformableNodes(contentSpec.getBaseLevel().getChildNodes()));
  
          // Merge the base level and comments
          final Map<SpecNode, CSNodeWrapper> nodeMapping = new HashMap<SpecNode, CSNodeWrapper>();
          mergeChildren(nodes, contentSpecNodes, providerFactory, null, contentSpecEntity, nodeMapping);
  
          contentSpecProvider.updateContentSpec(contentSpecEntity);
  
          // Merge the relationships now all nodes have a mapping to a database node
          mergeRelationships(nodeMapping, providerFactory);
  
          contentSpecProvider.updateContentSpec(contentSpecEntity, processorData.getLogMessage());
      }