();
            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.
     * 
     * 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 versionHistoryNodeRefs = new ArrayList();
        
        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 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 versionProperties = new HashMap();
        // Get the standard node details
        Map 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 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 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 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 versionAspectProperties = 
                versionable.getProperties().keySet();
            
            Map props = new HashMap();
            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.singletonMap(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR));
        }
        
        // Put Auditable back
        if(disableAuditable)
        {
            policyBehaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
        }
    }
    /**
     * @see org.alfresco.service.cmr.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 aspects = new HashSet(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);
                }
            }
            for (QName aspect : aspects)
            {
                this.nodeService.removeAspect(nodeRef, aspect);
            }
            // Re-add the versionable aspect to the reverted node
            if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == false)
            {
                this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, null);
            }
            // Re-set the version label property (since it should not be modified from the origional)
            this.nodeService.setProperty(nodeRef, ContentModel.PROP_VERSION_LABEL, currentVersionLabel);
            // Add/remove the child nodes
            List children = new ArrayList(this.nodeService.getChildAssocs(nodeRef));
            for (ChildAssociationRef versionedChild : this.nodeService.getChildAssocs(versionNodeRef))
            {
                if (children.contains(versionedChild) == false)
                {
                    if (this.nodeService.exists(versionedChild.getChildRef()) == true)
                    {
                        // The node was a primary child of the parent, but that is no longer the case.  Dispite this
                        // the node still exits so this means it has been moved.
                        // The best thing to do in this situation will be to re-add the node as a child, but it will not
                        // be a primary child.
                        this.nodeService.addChild(nodeRef, versionedChild.getChildRef(), versionedChild.getTypeQName(), versionedChild.getQName());
                    }
                    else
                    {
                        if (versionedChild.isPrimary() == true)
                        {
                            // Only try to resotre missing children if we are doing a deep revert
                            // Look and see if we have a version history for the child node
                            if (deep == true && getVersionHistoryNodeRef(versionedChild.getChildRef()) != null)
                            {
                                // We're going to try and restore the missing child node and recreate the assoc
                                restore(
                                   versionedChild.getChildRef(),
                                   nodeRef,
                                   versionedChild.getTypeQName(),
                                   versionedChild.getQName());
                            }
                            // else the deleted child did not have a version history so we can't restore the child
                            // and so we can't revert the association
                        }
                        // else
                        // Since this was never a primary assoc and the child has been deleted we won't recreate
                        // the missing node as it was never owned by the node and we wouldn't know where to put it.
                    }
                }
                else
                {
                    children.remove(versionedChild);
                }
            }
            for (ChildAssociationRef ref : children)
            {
                this.nodeService.removeChild(nodeRef, ref.getChildRef());
            }
            // Add/remove the target associations
            for (AssociationRef assocRef : this.nodeService.getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL))
            {
                this.nodeService.removeAssociation(assocRef.getSourceRef(), assocRef.getTargetRef(), assocRef.getTypeQName());
            }
            for (AssociationRef versionedAssoc : this.nodeService.getTargetAssocs(versionNodeRef, RegexQNamePattern.MATCH_ALL))
            {
                if (this.nodeService.exists(versionedAssoc.getTargetRef()) == true)
                {
                    this.nodeService.createAssociation(nodeRef, versionedAssoc.getTargetRef(), versionedAssoc.getTypeQName());
                }
                // else
                // Since the tareget of the assoc no longer exists we can't recreate the assoc
            }
        }
        finally
        {
            // Turn auto-version policies back on
            this.policyBehaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE);
        }
    }
    /**
     * @see org.alfresco.service.cmr.version.VersionService#restore(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName)
     */
     public NodeRef restore(
                NodeRef nodeRef,
                NodeRef parentNodeRef,
                QName assocTypeQName,
                QName assocQName)
     {
         return restore(nodeRef, parentNodeRef, assocTypeQName, assocQName, true);
     }
    /**
     * @see org.alfresco.service.cmr.version.VersionService#restore(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, boolean)
     */
     public NodeRef restore(
            NodeRef nodeRef,
            NodeRef parentNodeRef,
            QName assocTypeQName,
            QName assocQName,
            boolean deep)
    {
        if (logger.isDebugEnabled())
        {
            logger.debug("Run as user " + AuthenticationUtil.getRunAsUser());
            logger.debug("Fully authenticated " + AuthenticationUtil.getFullyAuthenticatedUser());
        }
         
        NodeRef restoredNodeRef = null;
        // Check that the node does not exist
        if (this.nodeService.exists(nodeRef) == true)
        {
            // Error since you can not restore a node that already exists
            throw new VersionServiceException(MSGID_ERR_RESTORE_EXISTS, new Object[]{nodeRef.toString()});
        }
        // Try and get the version details that we want to restore to
        Version version = getHeadVersion(nodeRef);
        if (version == null)
        {
            // Error since there is no version information available to restore the node from
            throw new VersionServiceException(MSGID_ERR_RESTORE_NO_VERSION, new Object[]{nodeRef.toString()});
        }
        // Set the uuid of the new node
        Map props = new HashMap(1);
        props.put(ContentModel.PROP_NODE_UUID, version.getVersionProperty(VersionModel.PROP_FROZEN_NODE_ID));
        // Get the type of the node node
        QName type = (QName)version.getVersionProperty(VersionModel.PROP_FROZEN_NODE_TYPE);
        // Disable auto-version behaviour
        this.policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_VERSIONABLE);
        try
        {
            // Create the restored node
            restoredNodeRef = this.nodeService.createNode(
                    parentNodeRef,
                    assocTypeQName,
                    assocQName,
                    type,
                    props).getChildRef();
        }
        finally
        {
            // Enable auto-version behaviour
            this.policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_VERSIONABLE);
        }
        // Now we need to revert the newly restored node
        revert(restoredNodeRef, version, deep);
        return restoredNodeRef;
    }
    /**
     * Get the head version given a node reference
     *
     * @param nodeRef   the node reference
     * @return          the 'head' version
     */
    private Version getHeadVersion(NodeRef nodeRef)
    {
        Version version = null;
        StoreRef storeRef = nodeRef.getStoreRef();
        NodeRef versionHistoryNodeRef = getVersionHistoryNodeRef(nodeRef);
        if (versionHistoryNodeRef != null)
        {
            List versionsAssoc = this.dbNodeService.getChildAssocs(versionHistoryNodeRef, RegexQNamePattern.MATCH_ALL, VersionModel.CHILD_QNAME_VERSIONS);
            for (ChildAssociationRef versionAssoc : versionsAssoc)
            {
                NodeRef versionNodeRef = versionAssoc.getChildRef();
                List successors = this.dbNodeService.getTargetAssocs(versionNodeRef, VersionModel.ASSOC_SUCCESSOR);
                if (successors.size() == 0)
                {
                    String storeProtocol = (String)this.dbNodeService.getProperty(
                            versionNodeRef,
                            QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_PROTOCOL));
                    String storeId = (String)this.dbNodeService.getProperty(
                            versionNodeRef,
                            QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_ID));
                    StoreRef versionStoreRef = new StoreRef(storeProtocol, storeId);
                    if (storeRef.equals(versionStoreRef) == true)
                    {
                        version = getVersion(versionNodeRef);
                    }
                }
            }
        }
        return version;
    }
    
    private Version getLatestVersion(NodeRef nodeRef)
    {
        Version version = null;
        StoreRef storeRef = nodeRef.getStoreRef();
        NodeRef versionHistoryNodeRef = getVersionHistoryNodeRef(nodeRef);
        if (versionHistoryNodeRef != null)
        {
            List versionsAssoc = this.dbNodeService.getChildAssocs(versionHistoryNodeRef, RegexQNamePattern.MATCH_ALL, VersionModel.CHILD_QNAME_VERSIONS);
            for (ChildAssociationRef versionAssoc : versionsAssoc)
            {
                NodeRef versionNodeRef = versionAssoc.getChildRef();
                List predecessors = this.dbNodeService.getSourceAssocs(versionNodeRef, VersionModel.ASSOC_SUCCESSOR);
                if (predecessors.size() == 0)
                {
                    String storeProtocol = (String)this.dbNodeService.getProperty(
                            versionNodeRef,
                            QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_PROTOCOL));
                    String storeId = (String)this.dbNodeService.getProperty(
                            versionNodeRef,
                            QName.createQName(NAMESPACE_URI, VersionModel.PROP_FROZEN_NODE_STORE_ID));
                    StoreRef versionStoreRef = new StoreRef(storeProtocol, storeId);
                    if (storeRef.equals(versionStoreRef) == true)
                    {
                        version = getVersion(versionNodeRef);
                    }
                }
            }
        }
        return version;
    }
    /**
     * @see VersionService#deleteVersionHistory(NodeRef)
     */
    public void deleteVersionHistory(NodeRef nodeRef)
        throws AspectMissingException
    {
        if (logger.isDebugEnabled())
        {
            logger.debug("Run as user " + AuthenticationUtil.getRunAsUser());
            logger.debug("Fully authenticated " + AuthenticationUtil.getFullyAuthenticatedUser());
        }
        
        // Get the version history node for the node is question and delete it
        NodeRef versionHistoryNodeRef = getVersionHistoryNodeRef(nodeRef);
        if (versionHistoryNodeRef != null)
        {
            // Delete the version history node
            this.dbNodeService.deleteNode(versionHistoryNodeRef);
            if (this.nodeService.exists(nodeRef) == true && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true)
            {
                // Reset the version label property on the versionable node
                this.nodeService.setProperty(nodeRef, ContentModel.PROP_VERSION_LABEL, null);
            }
        }
    }
    
    public void deleteVersion(NodeRef nodeRef, Version version)
    {
        // This operation is not supported for a version1 store
        throw new UnsupportedOperationException("Delete version is unsupported by the old (deprecated) version store implementation");
    }
    
    @Override
    protected void defaultOnCreateVersion(
            QName classRef,
            NodeRef nodeRef, 
            Map versionProperties, 
            PolicyScope nodeDetails)
    {
        ClassDefinition classDefinition = this.dictionaryService.getClass(classRef);    
        if (classDefinition != null)
        {           
            boolean wasMLAware = MLPropertyInterceptor.setMLAware(true);
            try
            {
                // Copy the properties
                Map propertyDefinitions = classDefinition.getProperties();
                for (QName propertyName : propertyDefinitions.keySet()) 
                {
                    Serializable propValue = this.nodeService.getProperty(nodeRef, propertyName);
                    nodeDetails.addProperty(classRef, propertyName, propValue);
                }
            }
            finally
            {
                MLPropertyInterceptor.setMLAware(wasMLAware);
            }
            
            // Version the associations (child and target)
            Map assocDefs = classDefinition.getAssociations();
            // TODO: Need way of getting child assocs of a given type
            if (classDefinition.isContainer())
            {
                List childAssocRefs = this.nodeService.getChildAssocs(nodeRef);
                for (ChildAssociationRef childAssocRef : childAssocRefs) 
                {
                    if (assocDefs.containsKey(childAssocRef.getTypeQName()))
                    {
                        nodeDetails.addChildAssociation(classDefinition.getName(), childAssocRef);
                    }
                }
            }
            
            // TODO: Need way of getting assocs of a given type
            List nodeAssocRefs = this.nodeService.getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL);
            for (AssociationRef nodeAssocRef : nodeAssocRefs) 
            {
                if (assocDefs.containsKey(nodeAssocRef.getTypeQName()))
                {
                    nodeDetails.addAssociation(classDefinition.getName(), nodeAssocRef);
                }
            }
        }
    }
	@Override
    public boolean isAVersion(NodeRef nodeRef)
    {
		throw new UnsupportedOperationException();
    }
	
	@Override
    public boolean isVersioned(NodeRef nodeRef)
    {
        if (logger.isDebugEnabled())
        {
            logger.debug("Run as user " + AuthenticationUtil.getRunAsUser());
            logger.debug("Fully authenticated " + AuthenticationUtil.getFullyAuthenticatedUser());
        }
	    
        return this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE);
    }
}