gt:             for (QName aspect : aspects)
context: /*
   * Copyright (C) 2005-2012 Alfresco Software Limited.
   *
   * This file is part of Alfresco
   *
   * Alfresco is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * Alfresco is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public License
   * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
   */
  package org.alfresco.repo.version;
  
  import java.io.Serializable;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import org.alfresco.error.AlfrescoRuntimeException;
  import org.alfresco.model.ContentModel;
  import org.alfresco.repo.node.MLPropertyInterceptor;
  import org.alfresco.repo.policy.BehaviourFilter;
  import org.alfresco.repo.policy.JavaBehaviour;
  import org.alfresco.repo.policy.PolicyScope;
  import org.alfresco.repo.security.authentication.AuthenticationUtil;
  import org.alfresco.repo.version.VersionServicePolicies.CalculateVersionLabelPolicy;
  import org.alfresco.repo.version.common.AbstractVersionServiceImpl;
  import org.alfresco.repo.version.common.VersionHistoryImpl;
  import org.alfresco.repo.version.common.VersionImpl;
  import org.alfresco.repo.version.common.VersionUtil;
  import org.alfresco.service.cmr.dictionary.AspectDefinition;
  import org.alfresco.service.cmr.dictionary.AssociationDefinition;
  import org.alfresco.service.cmr.dictionary.ClassDefinition;
  import org.alfresco.service.cmr.dictionary.PropertyDefinition;
  import org.alfresco.service.cmr.repository.AspectMissingException;
  import org.alfresco.service.cmr.repository.AssociationRef;
  import org.alfresco.service.cmr.repository.ChildAssociationRef;
  import org.alfresco.service.cmr.repository.NodeRef;
  import org.alfresco.service.cmr.repository.NodeService;
  import org.alfresco.service.cmr.repository.StoreRef;
  import org.alfresco.service.cmr.search.SearchService;
  import org.alfresco.service.cmr.version.ReservedVersionNameException;
  import org.alfresco.service.cmr.version.Version;
  import org.alfresco.service.cmr.version.VersionHistory;
  import org.alfresco.service.cmr.version.VersionService;
  import org.alfresco.service.cmr.version.VersionServiceException;
  import org.alfresco.service.cmr.version.VersionType;
  import org.alfresco.service.namespace.NamespaceService;
  import org.alfresco.service.namespace.QName;
  import org.alfresco.service.namespace.RegexQNamePattern;
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.springframework.extensions.surf.util.ParameterCheck;
  
  /**
   * Version1 Service - implements lightWeightVersionStore
   *
   * @author Roy Wetheral
   * 
   * NOTE: deprecated since 3.1 (migrate and use Version2 Service)
   */
  @SuppressWarnings("deprecation")
  public abstract class VersionServiceImpl extends AbstractVersionServiceImpl implements VersionService, VersionModel
  {
      private static Log logger = LogFactory.getLog(VersionServiceImpl.class);
      
      /**
       * Error message I18N id's
       */
      protected static final String MSGID_ERR_NOT_FOUND = "version_service.err_not_found";
      protected static final String MSGID_ERR_NO_BRANCHES = "version_service.err_unsupported";
      protected static final String MSGID_ERR_RESTORE_EXISTS = "version_service.err_restore_exists";
      protected static final String MSGID_ERR_ONE_PRECEEDING = "version_service.err_one_preceeding";
      protected static final String MSGID_ERR_RESTORE_NO_VERSION = "version_service.err_restore_no_version";
      protected static final String MSGID_ERR_REVERT_MISMATCH = "version_service.err_revert_mismatch";
  
      /**
       * The db node service, used as the version store implementation
       */
      protected NodeService dbNodeService;
  
      /**
       * Policy behaviour filter
       */
      protected BehaviourFilter policyBehaviourFilter;
  
      /**
       * The repository searcher
       */
      protected SearchService searcher; // unused
  
      protected Comparator<Version> versionComparatorDesc;
  
      /**
       * Sets the db node service, used as the version store implementation
       *
       * @param nodeService  the node service
       */
      public void setDbNodeService(NodeService nodeService)
      {
          this.dbNodeService = nodeService;
      }
  
      /**
       * @param searcher  the searcher
       */
      public void setSearcher(SearchService searcher)
      {
          this.searcher = searcher;
      }
      
      /**
       * Set the policy behaviour filter
       *
       * @param policyBehaviourFilter     the policy behaviour filter
       */
      public void setPolicyBehaviourFilter(BehaviourFilter policyBehaviourFilter)
      {
          this.policyBehaviourFilter = policyBehaviourFilter;
      }
  
      /**
       * Sets an optional comparator to sort a versions in descending order (eg. 2.1, 2.0, 1.1, 1.0).
       * Really only needed in a 4.1.3 system (or above) that has been upgraded from an earlier system
       * that used NON ordered sequence numbers in a cluster. Not something we really support but was
       * the subject of MNT-226.
       * @param versionComparatorClass the name of a comparator. For example
       *        "org.alfresco.repo.version.common.VersionLabelComparator".
       */
      @SuppressWarnings("unchecked")
      public void setVersionComparatorClass(String versionComparatorClass)
      {
          if (versionComparatorClass != null && versionComparatorClass.trim().length() != 0)
          {
              try
              {
                  versionComparatorDesc = (Comparator<Version>) getClass().getClassLoader().
                          loadClass(versionComparatorClass.trim()).newInstance();
              }
              catch (Exception e)
              {
                  throw new AlfrescoRuntimeException(
                          "Failed to create a Comparator<Version> using the class name "+
                          versionComparatorClass, e);
              }
          }
      }
      
      /**
       * Register version label policy for the specified type
       * 
       * @param typeQName
       * @param policy 
       */
      public void registerVersionLabelPolicy(QName typeQName, CalculateVersionLabelPolicy policy)
      {
          // Register the serial version label behaviour
          this.policyComponent.bindClassBehaviour(
                  QName.createQName(NamespaceService.ALFRESCO_URI, "calculateVersionLabel"),
                  typeQName,
                  new JavaBehaviour(policy, "calculateVersionLabel"));
      }
      
      /**
       * Initialise method
       */
      @Override
      public void initialise()
      {
          super.initialise();     
      }
      
      /**
       * Gets the reference to the version store
       *
       * @return  reference to the version store
       */
      @Override
      public StoreRef getVersionStoreReference()
      {
          if (logger.isDebugEnabled())
          {
              logger.debug("Run as user " + AuthenticationUtil.getRunAsUser());
              logger.debug("Fully authenticated " + AuthenticationUtil.getFullyAuthenticatedUser());
          }
          
          return new StoreRef(
                  StoreRef.PROTOCOL_WORKSPACE,
                  VersionModel.STORE_ID);
      }
  
      /**
       * @see VersionService#createVersion(NodeRef, Map)
       */
      public Version createVersion(
              NodeRef nodeRef,
              Map<String, Serializable> versionProperties)
              throws ReservedVersionNameException, AspectMissingException
      {
          if (logger.isDebugEnabled())
          {
              logger.debug("Run as user " + AuthenticationUtil.getRunAsUser());
              logger.debug("Fully authenticated " + AuthenticationUtil.getFullyAuthenticatedUser());
          }
          
          long startTime = System.currentTimeMillis();
          
          int versionNumber = 0; // deprecated (unused)
          
          // Create the version
          Version version = createVersion(nodeRef, versionProperties, versionNumber);
          
          if (logger.isDebugEnabled())
          {
              logger.debug("created version (" + VersionUtil.convertNodeRef(version.getFrozenStateNodeRef()) + ") in " + (System.currentTimeMillis()-startTime) + " ms");
          }
          
          return version;
      }
  
      /**
       * The version's are created from the children upwards with the parent being created first.  This will
       * ensure that the child version references in the version node will point to the version history nodes
       * for the (possibly) newly created version histories.
       */
      public Collection<Version> createVersion(
              NodeRef nodeRef,
              Map<String, Serializable> versionProperties,
              boolean versionChildren)
              throws ReservedVersionNameException, AspectMissingException
      {
          if (logger.isDebugEnabled())
          {
              logger.debug("Run as user " + AuthenticationUtil.getRunAsUser());
              logger.debug("Fully authenticated " + AuthenticationUtil.getFullyAuthenticatedUser());
          }
          
          long startTime = System.currentTimeMillis();
          
          int versionNumber = 0; // deprecated (unused)
          
          // Create the versions
          Collection<Version> versions = createVersion(nodeRef, versionProperties, versionChildren, versionNumber);
          
          if (logger.isDebugEnabled())
          {
              Version[] versionsArray = versions.toArray(new Version[0]);
              Version version = versionsArray[versionsArray.length -1]; // last item is the new parent version
              logger.debug("created version (" + VersionUtil.convertNodeRef(version.getFrozenStateNodeRef()) + ") in "+ (System.currentTimeMillis()-startTime) +" ms "+(versionChildren ? "(with " + (versions.size() - 1) + " children)" : ""));
          }
          
          return versions;
      }
  
      /**
       * Helper method used to create the version when the versionChildren flag is provided.  This method
       * ensures that all the children (if the falg is set to true) are created with the same version
       * number, this ensuring that the version stripe is correct.
       *
       * @param nodeRef                           the parent node reference
       * @param versionProperties                 the version properties
       * @param versionChildren                   indicates whether to version the children of the parent
       *                                          node
       * @param versionNumber                     the version number
  
       * @return                                  a collection of the created versions
       * @throws ReservedVersionNameException     thrown if there is a reserved version property name clash
       * @throws AspectMissingException    thrown if the version aspect is missing from a node
       */
      private Collection<Version> createVersion(
              NodeRef nodeRef,
              Map<String, Serializable> versionProperties,
              boolean versionChildren,
              int versionNumber)
              throws ReservedVersionNameException, AspectMissingException
      {
  
          Collection<Version> result = new ArrayList<Version>();
  
          if (versionChildren == true)
          {
              // Get the children of the node
              Collection<ChildAssociationRef> children = this.dbNodeService.getChildAssocs(nodeRef);
              for (ChildAssociationRef childAssoc : children)
              {
                  // Recurse into this method to version all the children with the same version number
                  Collection<Version> childVersions = createVersion(
                          childAssoc.getChildRef(),
                          versionProperties,
                          versionChildren,
                          versionNumber);
                  result.addAll(childVersions);
              }
          }
  
          result.add(createVersion(nodeRef, versionProperties, versionNumber));
  
          return result;
      }
  
      /**
       * Note:  we can't control the order of the list, so if we have children and parents in the list and the
       * parents get versioned before the children and the children are not already versioned then the parents
       * child references will be pointing to the node ref, rather than the verison history.
       */
      public Collection<Version> createVersion(
              Collection<NodeRef> nodeRefs,
              Map<String, Serializable> versionProperties)
              throws ReservedVersionNameException, AspectMissingException
      {
          if (logger.isDebugEnabled())
          {
              logger.debug("Run as user " + AuthenticationUtil.getRunAsUser());
              logger.debug("Fully authenticated " + AuthenticationUtil.getFullyAuthenticatedUser());
          }
          
          long startTime = System.currentTimeMillis();
          
          Collection<Version> result = new ArrayList<Version>(nodeRefs.size());
          
          int versionNumber = 0; // deprecated (unused)
          
          // Version each node in the list
          for (NodeRef nodeRef : nodeRefs)
          {
              result.add(createVersion(nodeRef, versionProperties, versionNumber));
          }
  
          if (logger.isDebugEnabled())
          {
              logger.debug("created version list (" + getVersionStoreReference() + ") in "+ (System.currentTimeMillis()-startTime) +" ms (with " + nodeRefs.size() + " nodes)");
          }
          
          return result;
      }
  
      /**
       * Creates a new version of the passed node assigning the version properties
       * accordingly.
       *
       * @param  nodeRef              a node reference
       * @param  origVersionProperties    the version properties
       * @param  versionNumber        the version number
       * @return                      the newly created version
       * @throws ReservedVersionNameException
       *                              thrown if there is a name clash in the version properties
       */
      protected Version createVersion(
              NodeRef nodeRef,
              Map<String, Serializable> origVersionProperties,
              int versionNumber)
              throws ReservedVersionNameException
      {
          long startTime = System.currentTimeMillis();
  
          // Copy the version properties (to prevent unexpected side effects to the caller)
          Map<String, Serializable> versionProperties = new HashMap<String, Serializable>();
          if (origVersionProperties != null)
          {
              versionProperties.putAll(origVersionProperties);
          }
  
          // If the version aspect is not there then add it
          if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == false)
          {
              this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, null);
          }
  
          // Call the policy behaviour
          invokeBeforeCreateVersion(nodeRef);
  
          // Check that the supplied additional version properties do not clash with the reserved ones
          VersionUtil.checkVersionPropertyNames(versionProperties.keySet());
  
          // Check the repository for the version history for this node
          NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef);
          NodeRef currentVersionRef = null;
  
          if (versionHistoryRef == null)
          {
              // Create the version history
              versionHistoryRef = createVersionHistory(nodeRef);
          }
          else
          {
              // Since we have an exisiting version history we should be able to lookup
              // the current version
              currentVersionRef = getCurrentVersionNodeRef(versionHistoryRef, nodeRef);
  
              if (currentVersionRef == null)
              {
                  throw new VersionServiceException(MSGID_ERR_NOT_FOUND);
              }
  
              // Need to check that we are not about to create branch since this is not currently supported
              VersionHistory versionHistory = buildVersionHistory(versionHistoryRef, nodeRef);
              Version currentVersion = getVersion(currentVersionRef);
              if (versionHistory.getSuccessors(currentVersion).size() != 0)
              {
                  throw new VersionServiceException(MSGID_ERR_NO_BRANCHES);
              }
          }
  
          // Create the node details
          QName classRef = this.nodeService.getType(nodeRef);
          PolicyScope nodeDetails = new PolicyScope(classRef);
  
          // Get the node details by calling the onVersionCreate policy behaviour
          invokeOnCreateVersion(nodeRef, versionProperties, nodeDetails);
  
          // Create the new version node (child of the version history)
          NodeRef newVersionRef = createNewVersion(
                  nodeRef,
                  versionHistoryRef,
                  getStandardVersionProperties(versionProperties, nodeRef, currentVersionRef, versionNumber),
                  versionProperties,
                  nodeDetails);
  
          if (currentVersionRef == null)
          {
              // Set the new version to be the root version in the version history
              this.dbNodeService.createAssociation(
                      versionHistoryRef,
                      newVersionRef,
                      VersionServiceImpl.ASSOC_ROOT_VERSION);
          }
          else
          {
              // Relate the new version to the current version as its successor
              this.dbNodeService.createAssociation(
                      currentVersionRef,
                      newVersionRef,
                      VersionServiceImpl.ASSOC_SUCCESSOR);
          }
  
          // Create the version data object
          Version version = this.getVersion(newVersionRef);
  
          // Set the new version label on the versioned node
          this.nodeService.setProperty(
                  nodeRef,
                  ContentModel.PROP_VERSION_LABEL,
                  version.getVersionLabel());
  
          // Freeze the version label property
          Map<QName, Serializable> versionLabelAsMap = new HashMap<QName, Serializable>(1);
          versionLabelAsMap.put(ContentModel.PROP_VERSION_LABEL, version.getVersionLabel());
          this.freezeProperties(newVersionRef, versionLabelAsMap);
  
          // Invoke the policy behaviour
          invokeAfterCreateVersion(nodeRef, version);
  
          if (logger.isTraceEnabled())
          {
              logger.trace("created Version (" + getVersionStoreReference() + ") " + nodeRef + " in " + (System.currentTimeMillis()-startTime) +" ms");
          }
          
          // Return the data object representing the newly created version
          return version;
      }
  
      /**
       * Creates a new version history node, applying the root version aspect is required
       *
       * @param nodeRef   the node ref
       * @return          the version history node reference
       */
      private NodeRef createVersionHistory(NodeRef nodeRef)
      {
          HashMap<QName, Serializable> props = new HashMap<QName, Serializable>();
          props.put(ContentModel.PROP_NAME, nodeRef.getId());
          props.put(PROP_QNAME_VERSIONED_NODE_ID, nodeRef.getId());
  
          // Create a new version history node
          ChildAssociationRef childAssocRef = this.dbNodeService.createNode(
                  getRootNode(),
                  CHILD_QNAME_VERSION_HISTORIES,
                  QName.createQName(VersionModel.NAMESPACE_URI, nodeRef.getId()),
                  TYPE_QNAME_VERSION_HISTORY,
                  props);
          return childAssocRef.getChildRef();
      }
  
      /**
       * @see org.alfresco.service.cmr.version.VersionService#getVersionHistory(NodeRef)
       */
      public VersionHistory getVersionHistory(NodeRef nodeRef)
      {
          if (logger.isDebugEnabled())
          {
              logger.debug("Run as user " + AuthenticationUtil.getRunAsUser());
              logger.debug("Fully authenticated " + AuthenticationUtil.getFullyAuthenticatedUser());
          }
          
          VersionHistory versionHistory = null;
  
          if (this.nodeService.exists(nodeRef) == true)
          {
              NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef);
              if (versionHistoryRef != null)
              {
                  versionHistory = buildVersionHistory(versionHistoryRef, nodeRef);
              }
          }
  
          return versionHistory;
      }
  
      /**
       * @see VersionService#getCurrentVersion(NodeRef)
       */
      public Version getCurrentVersion(NodeRef nodeRef)
      {
          if (logger.isDebugEnabled())
          {
              logger.debug("Run as user " + AuthenticationUtil.getRunAsUser());
              logger.debug("Fully authenticated " + AuthenticationUtil.getFullyAuthenticatedUser());
          }
          
          Version version = null;
  
          if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true)
          {
              VersionHistory versionHistory = getVersionHistory(nodeRef);
              if (versionHistory != null)
              {
                  String versionLabel = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL);
                  version = versionHistory.getVersion(versionLabel);
              }
          }
  
          return version;
      }
  
      /**
       * Get a map containing the standard list of version properties populated.
       *
       * @param versionProperties     the version meta data properties
       * @param nodeRef               the node reference
       * @param preceedingNodeRef     the preceeding node reference
       * @param versionNumber         the version number
       * @return                      the standard version properties
       */
      private Map<QName, Serializable> getStandardVersionProperties(Map<String, Serializable> versionProperties, NodeRef nodeRef, NodeRef preceedingNodeRef, int versionNumber)
      {
          Map<QName, Serializable> result = new HashMap<QName, Serializable>(10);
  
          // deprecated (unused)
          //result.put(VersionModel.PROP_QNAME_VERSION_NUMBER, Integer.toString(versionNumber));
  
          // Set the versionable node id
          result.put(VersionModel.PROP_QNAME_FROZEN_NODE_ID, nodeRef.getId());
  
          // Set the versionable node store protocol
          result.put(VersionModel.PROP_QNAME_FROZEN_NODE_STORE_PROTOCOL, nodeRef.getStoreRef().getProtocol());
  
          // Set the versionable node store id
          result.put(VersionModel.PROP_QNAME_FROZEN_NODE_STORE_ID, nodeRef.getStoreRef().getIdentifier());
  
          // Store the current node type
          QName nodeType = this.nodeService.getType(nodeRef);
          result.put(VersionModel.PROP_QNAME_FROZEN_NODE_TYPE, nodeType);
  
          // Store the current aspects
          Set<QName> aspects = this.nodeService.getAspects(nodeRef);
          result.put(VersionModel.PROP_QNAME_FROZEN_ASPECTS, (Serializable)aspects);
  
          // Calculate the version label
          QName classRef = this.nodeService.getType(nodeRef);
          Version preceedingVersion = getVersion(preceedingNodeRef);
          String versionLabel = invokeCalculateVersionLabel(classRef, preceedingVersion, versionNumber, versionProperties);
          result.put(VersionModel.PROP_QNAME_VERSION_LABEL, versionLabel);
  
          return result;
      }
  
      /**
       * Creates a new version node, setting the properties both calculated and specified.
       *
       * @param versionableNodeRef  the reference to the node being versioned
       * @param versionHistoryRef   version history node reference
       * @param standardVersionProperties   version properties
       * @param versionProperties   version properties
       * @param nodeDetails          PolicyScope
       * @return                    the version node reference
       */
      private NodeRef createNewVersion(
              NodeRef versionableNodeRef,
              NodeRef versionHistoryRef,
              Map<QName, Serializable> standardVersionProperties,
              Map<String, Serializable> versionProperties,
              PolicyScope nodeDetails)
      {
          // Create the new version
          ChildAssociationRef childAssocRef = this.dbNodeService.createNode(
                  versionHistoryRef,
                  CHILD_QNAME_VERSIONS,
                  CHILD_QNAME_VERSIONS,
                  TYPE_QNAME_VERSION,
                  standardVersionProperties);
          NodeRef versionNodeRef = childAssocRef.getChildRef();
  
          // Store the meta data
          storeVersionMetaData(versionNodeRef, versionProperties);
  
          // Freeze the various parts of the node
          freezeProperties(versionNodeRef, nodeDetails.getProperties());
          freezeChildAssociations(versionNodeRef, nodeDetails.getChildAssociations());
          freezeAssociations(versionNodeRef, nodeDetails.getAssociations());
          freezeAspects(nodeDetails, versionNodeRef, nodeDetails.getAspects());
  
          // Return the created node reference
          return versionNodeRef;
      }
  
      /**
       * Store the version meta data
       *
       * @param versionNodeRef        the version node reference
       * @param versionProperties     the version properties
       */
      private void storeVersionMetaData(NodeRef versionNodeRef, Map<String, Serializable> versionProperties)
      {
          for (Map.Entry<String, Serializable> entry : versionProperties.entrySet())
          {
              HashMap<QName, Serializable> properties = new HashMap<QName, Serializable>();
  
              properties.put(PROP_QNAME_META_DATA_NAME, entry.getKey());
              properties.put(PROP_QNAME_META_DATA_VALUE, entry.getValue());
  
              this.dbNodeService.createNode(
                      versionNodeRef,
                      CHILD_QNAME_VERSION_META_DATA,
                      CHILD_QNAME_VERSION_META_DATA,
                      TYPE_QNAME_VERSION_META_DATA_VALUE,
                      properties);
          }
      }
      
      protected Map<String, Serializable> getVersionMetaData(NodeRef versionNodeRef)
      {
          // Get the meta data
          List<ChildAssociationRef> metaData = this.dbNodeService.getChildAssocs(
                  versionNodeRef,
                  RegexQNamePattern.MATCH_ALL,
                  CHILD_QNAME_VERSION_META_DATA);
          
          Map<String, Serializable> versionProperties = new HashMap<String, Serializable>(metaData.size());
          
          for (ChildAssociationRef ref : metaData)
          {
              NodeRef metaDataValue = (NodeRef)ref.getChildRef();
              String name = (String)this.dbNodeService.getProperty(metaDataValue, PROP_QNAME_META_DATA_NAME);
              Serializable value = this.dbNodeService.getProperty(metaDataValue, PROP_QNAME_META_DATA_VALUE);
              versionProperties.put(name, value);
          }
          
          return versionProperties;
      }
  
      /**
       * Freeze the aspects
       *
       * @param nodeDetails      the node details
       * @param versionNodeRef   the version node reference
       * @param aspects          the set of aspects
       */
      private void freezeAspects(PolicyScope nodeDetails, NodeRef versionNodeRef, Set<QName> aspects)
      {
          for (QName aspect : aspects)
          {
              // Freeze the details of the aspect
              freezeProperties(versionNodeRef, nodeDetails.getProperties(aspect));
              freezeChildAssociations(versionNodeRef, nodeDetails.getChildAssociations(aspect));
              freezeAssociations(versionNodeRef, nodeDetails.getAssociations(aspect));
          }
      }
  
      /**
       * Freeze associations
       *
       * @param versionNodeRef   the version node reference
       * @param associations     the list of associations
       */
      private void freezeAssociations(NodeRef versionNodeRef, List<AssociationRef> associations)
      {
          for (AssociationRef targetAssoc : associations)
          {
              HashMap<QName, Serializable> properties = new HashMap<QName, Serializable>();
  
              // Set the qname of the association
              properties.put(PROP_QNAME_ASSOC_TYPE_QNAME, targetAssoc.getTypeQName());
  
              // Set the reference property to point to the child node
              properties.put(ContentModel.PROP_REFERENCE, targetAssoc.getTargetRef());
  
              // Create child version reference
              this.dbNodeService.createNode(
                      versionNodeRef,
                      CHILD_QNAME_VERSIONED_ASSOCS,
                      CHILD_QNAME_VERSIONED_ASSOCS,
                      TYPE_QNAME_VERSIONED_ASSOC,
                      properties);
          }
      }
  
      /**
       * Freeze child associations
       *
       * @param versionNodeRef       the version node reference
       * @param childAssociations    the child associations
       */
      private void freezeChildAssociations(NodeRef versionNodeRef, List<ChildAssociationRef> childAssociations)
      {
          for (ChildAssociationRef childAssocRef : childAssociations)
          {
              HashMap<QName, Serializable> properties = new HashMap<QName, Serializable>();
  
              // Set the qname, isPrimary and nthSibling properties
              properties.put(PROP_QNAME_ASSOC_QNAME, childAssocRef.getQName());
              properties.put(PROP_QNAME_ASSOC_TYPE_QNAME, childAssocRef.getTypeQName());
              properties.put(PROP_QNAME_IS_PRIMARY, Boolean.valueOf(childAssocRef.isPrimary()));
              properties.put(PROP_QNAME_NTH_SIBLING, Integer.valueOf(childAssocRef.getNthSibling()));
  
              // Set the reference property to point to the child node
              properties.put(ContentModel.PROP_REFERENCE, childAssocRef.getChildRef());
  
              // Create child version reference
              this.dbNodeService.createNode(
                      versionNodeRef,
                      CHILD_QNAME_VERSIONED_CHILD_ASSOCS,
                      CHILD_QNAME_VERSIONED_CHILD_ASSOCS,
                      TYPE_QNAME_VERSIONED_CHILD_ASSOC,
                      properties);
          }
      }
  
      /**
       * Freeze properties
       *
       * @param versionNodeRef   the version node reference
       * @param properties       the properties
       */
      private void freezeProperties(NodeRef versionNodeRef, Map<QName, Serializable> properties)
      {
          // Copy the property values from the node onto the version node
          for (Map.Entry<QName, Serializable> entry : properties.entrySet())
          {
              // Get the property values
              HashMap<QName, Serializable> props = new HashMap<QName, Serializable>();
              props.put(PROP_QNAME_QNAME, entry.getKey());
  
              if (entry.getValue() instanceof Collection)
              {
                  props.put(PROP_QNAME_MULTI_VALUE, entry.getValue());
                  props.put(PROP_QNAME_IS_MULTI_VALUE, true);
              }
              else
              {
                  props.put(PROP_QNAME_VALUE, entry.getValue());
                  props.put(PROP_QNAME_IS_MULTI_VALUE, false);
              }
  
              // Create the node storing the frozen attribute details
              this.dbNodeService.createNode(
                      versionNodeRef,
                      CHILD_QNAME_VERSIONED_ATTRIBUTES,
                      CHILD_QNAME_VERSIONED_ATTRIBUTES,
                      TYPE_QNAME_VERSIONED_PROPERTY,
                      props);
          }
      }
  
      /**
       * Gets the version stores root node
       *
       * @return the node ref to the root node of the version store
       */
      protected NodeRef getRootNode()
      {
          // Get the version store root node reference
          return this.dbNodeService.getRootNode(getVersionStoreReference());
      }
  
      /**
       * Builds a version history object from the version history reference.
       * <p>
       * The node ref is passed to enable the version history to be scoped to the
       * appropriate branch in the version history.
       *
       * @param versionHistoryRef  the node ref for the version history
       * @param nodeRef            the node reference
       * @return                   a constructed version history object
       */
      protected VersionHistory buildVersionHistory(NodeRef versionHistoryRef, NodeRef nodeRef)
      {
          VersionHistory versionHistory = null;
  
          // List of versions with current one last and root one first.
          ArrayList<NodeRef> versionHistoryNodeRefs = new ArrayList<NodeRef>();
          
          NodeRef currentVersion;
          if (this.nodeService.exists(nodeRef))
          {
              currentVersion = getCurrentVersionNodeRef(versionHistoryRef, nodeRef);
          }
          else
          {
              currentVersion = VersionUtil.convertNodeRef(getLatestVersion(nodeRef).getFrozenStateNodeRef());
          }
  
          while (currentVersion != null)
          {
              AssociationRef preceedingVersion = null;
  
              versionHistoryNodeRefs.add(0, currentVersion);
  
              List<AssociationRef> preceedingVersions = this.dbNodeService.getSourceAssocs(
                                                                                  currentVersion,
                                                                                  VersionModel.ASSOC_SUCCESSOR);
              if (preceedingVersions.size() == 1)
              {
                  preceedingVersion = (AssociationRef)preceedingVersions.toArray()[0];
                  currentVersion = preceedingVersion.getSourceRef();
              }
              else if (preceedingVersions.size() > 1)
              {
                  // Error since we only currently support one preceeding version
                  throw new VersionServiceException(MSGID_ERR_ONE_PRECEEDING);
              }
              else
              {
                  currentVersion = null;
              }
          }
          
          // Build the version history object
          boolean isRoot = true;
          Version preceeding = null;
          for (NodeRef versionRef : versionHistoryNodeRefs)
          {
              Version version = getVersion(versionRef);
  
              if (isRoot == true)
              {
                  versionHistory = new VersionHistoryImpl(version, versionComparatorDesc);
                  isRoot = false;
              }
              else
              {
                  ((VersionHistoryImpl)versionHistory).addVersion(version, preceeding);
              }
              preceeding = version;
          }
  
          return versionHistory;
      }
  
      /**
       * Constructs the a version object to contain the version information from the version node ref.
       *
       * @param versionRef  the version reference
       * @return            object containing verison data
       */
      protected Version getVersion(NodeRef versionRef)
      {
          if (versionRef == null)
          {
              return null;
          }
          Map<String, Serializable> versionProperties = new HashMap<String, Serializable>();
  
          // Get the standard node details
          Map<QName, Serializable> nodeProperties = this.dbNodeService.getProperties(versionRef);
          for (QName key : nodeProperties.keySet())
          {
              Serializable value = nodeProperties.get(key);
              versionProperties.put(key.getLocalName(), value);
          }
  
          // Get the meta data
          Map<String, Serializable> versionMetaDataProperties = getVersionMetaData(versionRef);
          versionProperties.putAll(versionMetaDataProperties);
          
          // Create and return the version object
          NodeRef newNodeRef = new NodeRef(new StoreRef(STORE_PROTOCOL, STORE_ID), versionRef.getId());
          Version result = new VersionImpl(versionProperties, newNodeRef);
          // done
          return result;
      }
  
      /**
       * Gets a reference to the version history node for a given 'real' node.
       *
       * @param nodeRef  a node reference
       * @return         a reference to the version history node, null of none
       */
      protected NodeRef getVersionHistoryNodeRef(NodeRef nodeRef)
      {
          if (nodeService.exists(nodeRef))
          {
              return this.dbNodeService.getChildByName(getRootNode(), CHILD_QNAME_VERSION_HISTORIES, (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_UUID));
          }
          else
          {
              return this.dbNodeService.getChildByName(getRootNode(), CHILD_QNAME_VERSION_HISTORIES, nodeRef.getId());
          }
      }
  
      /**
       * Gets a reference to the node for the current version of the passed node ref.
       *
       * This uses the version label as a mechanism for looking up the version node in
       * the version history.
       *
       * @param nodeRef  a node reference
       * @return         a reference to a version reference
       */
      private NodeRef getCurrentVersionNodeRef(NodeRef versionHistory, NodeRef nodeRef)
      {
          NodeRef result = null;
          String versionLabel = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL);
  
          Collection<ChildAssociationRef> versions = this.dbNodeService.getChildAssocs(versionHistory);
          for (ChildAssociationRef version : versions)
          {
              String tempLabel = (String)this.dbNodeService.getProperty(version.getChildRef(), VersionModel.PROP_QNAME_VERSION_LABEL);
              if (tempLabel != null && tempLabel.equals(versionLabel) == true)
              {
                  result = version.getChildRef();
                  break;
              }
          }
  
          return result;
      }
      
      /**
       * @see VersionService#ensureVersioningEnabled(NodeRef,Map)
       */
      public void ensureVersioningEnabled(NodeRef nodeRef, Map<QName, Serializable> versionProperties)
      {
          if (logger.isDebugEnabled())
          {
              logger.debug("Run as user " + AuthenticationUtil.getRunAsUser());
              logger.debug("Fully authenticated " + AuthenticationUtil.getFullyAuthenticatedUser());
          }
          
          // Don't alter the auditable aspect!
          boolean disableAuditable = policyBehaviourFilter.isEnabled(ContentModel.ASPECT_AUDITABLE);
          if(disableAuditable)
          {
              policyBehaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
          }
          
          // Do we need to apply the aspect?
          if (! nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE))
          {
              // Only apply new properties that are version ones
              AspectDefinition versionable =
                  dictionaryService.getAspect(ContentModel.ASPECT_VERSIONABLE);
              Set<QName> versionAspectProperties = 
                  versionable.getProperties().keySet();
              
              Map<QName,Serializable> props = new HashMap<QName, Serializable>();
              if(versionProperties != null &&! versionProperties.isEmpty())
              {
                  for(QName prop : versionProperties.keySet())
                  {
                      if(versionAspectProperties.contains(prop))
                      {
                          // This property is one from the versionable aspect
                          props.put(prop, versionProperties.get(prop));
                      }
                  }
              }
              
              // Add the aspect
              nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, props);
          }
          
          // Do we need to create the initial version history entry? By convention this is always a major version.
          if(getVersionHistoryNodeRef(nodeRef) == null)
          {
              createVersion(nodeRef, Collections.<String,Serializable>singletonMap(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR));
          }
          
          // Put Auditable back
          if(disableAuditable)
          {
              policyBehaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
          }
      }
  
      /**
       * @see org.alfresco.cms.version.VersionService#revert(NodeRef)
       */
      public void revert(NodeRef nodeRef)
      {
          revert(nodeRef, getCurrentVersion(nodeRef), true);
      }
  
      /**
       * @see org.alfresco.service.cmr.version.VersionService#revert(org.alfresco.service.cmr.repository.NodeRef, boolean)
       */
      public void revert(NodeRef nodeRef, boolean deep)
      {
          revert(nodeRef, getCurrentVersion(nodeRef), deep);
      }
  
      /**
       * @see org.alfresco.service.cmr.version.VersionService#revert(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.version.Version)
       */
      public void revert(NodeRef nodeRef, Version version)
      {
          revert(nodeRef, version, true);
      }
  
      /**
       * @see org.alfresco.service.cmr.version.VersionService#revert(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.version.Version, boolean)
       */
      public void revert(NodeRef nodeRef, Version version, boolean deep)
      {
          if (logger.isDebugEnabled())
          {
              logger.debug("Run as user " + AuthenticationUtil.getRunAsUser());
              logger.debug("Fully authenticated " + AuthenticationUtil.getFullyAuthenticatedUser());
          }
          
          // Check the mandatory parameters
          ParameterCheck.mandatory("nodeRef", nodeRef);
          ParameterCheck.mandatory("version", version);
  
          // Cross check that the version provided relates to the node reference provided
          if (nodeRef.getId().equals(version.getVersionProperty(VersionModel.PROP_FROZEN_NODE_ID)) == false)
          {
              // Error since the version provided does not correspond to the node reference provided
              throw new VersionServiceException(MSGID_ERR_REVERT_MISMATCH);
          }
  
          // Turn off any auto-version policy behaviours
          this.policyBehaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE);
          try
          {
              // Store the current version label
              String currentVersionLabel = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL);
  
              // Get the node that represents the frozen state
              NodeRef versionNodeRef = version.getFrozenStateNodeRef();
  
              // Revert the property values
              this.nodeService.setProperties(nodeRef, this.nodeService.getProperties(versionNodeRef));
  
              // Apply/remove the aspects as required
              Set<QName> aspects = new HashSet<QName>(this.nodeService.getAspects(nodeRef));
              for (QName versionAspect : this.nodeService.getAspects(versionNodeRef))
              {
                  if (aspects.contains(versionAspect) == false)
                  {
                      this.nodeService.addAspect(nodeRef, versionAspect, null);
                  }
                  else
                  {
                      aspects.remove(versionAspect);
                  }
              }
  