/*
* Copyright (C) 2009 eXo Platform SAS.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.services.jcr.impl.core.version;
import org.exoplatform.services.jcr.access.AccessControlEntry;
import org.exoplatform.services.jcr.access.AccessControlList;
import org.exoplatform.services.jcr.access.SystemIdentity;
import org.exoplatform.services.jcr.core.nodetype.NodeTypeDataManager;
import org.exoplatform.services.jcr.dataflow.ItemState;
import org.exoplatform.services.jcr.datamodel.IllegalNameException;
import org.exoplatform.services.jcr.datamodel.InternalQName;
import org.exoplatform.services.jcr.datamodel.ItemData;
import org.exoplatform.services.jcr.datamodel.ItemType;
import org.exoplatform.services.jcr.datamodel.NodeData;
import org.exoplatform.services.jcr.datamodel.PropertyData;
import org.exoplatform.services.jcr.datamodel.QPath;
import org.exoplatform.services.jcr.datamodel.QPathEntry;
import org.exoplatform.services.jcr.datamodel.ValueData;
import org.exoplatform.services.jcr.impl.Constants;
import org.exoplatform.services.jcr.impl.core.SessionImpl;
import org.exoplatform.services.jcr.impl.dataflow.AbstractItemDataCopyVisitor;
import org.exoplatform.services.jcr.impl.dataflow.ItemDataCopyVisitor;
import org.exoplatform.services.jcr.impl.dataflow.ItemDataRemoveVisitor;
import org.exoplatform.services.jcr.impl.dataflow.TransientItemData;
import org.exoplatform.services.jcr.impl.dataflow.TransientNodeData;
import org.exoplatform.services.jcr.impl.dataflow.TransientPropertyData;
import org.exoplatform.services.jcr.impl.dataflow.TransientValueData;
import org.exoplatform.services.jcr.impl.dataflow.session.SessionChangesLog;
import org.exoplatform.services.jcr.impl.dataflow.version.VersionHistoryDataHelper;
import org.exoplatform.services.jcr.impl.storage.JCRItemExistsException;
import org.exoplatform.services.jcr.util.IdGenerator;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import javax.jcr.ItemExistsException;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.version.OnParentVersionAction;
/**
* Created by The eXo Platform SAS.
*
* 14.12.2006
*
* @author <a href="mailto:peter.nedonosko@exoplatform.com.ua">Peter Nedonosko</a>
* @version $Id: ItemDataRestoreVisitor.java 14100 2008-05-12 10:53:47Z gazarenkov $
*/
public class ItemDataRestoreVisitor extends AbstractItemDataCopyVisitor
{
private final Log log = ExoLogger.getLogger("exo.jcr.component.core.ItemDataRestoreVisitor");
protected final boolean removeExisting;
protected final Stack<NodeDataContext> parents = new Stack<NodeDataContext>();
protected final NodeData context;
protected final NodeData history;
protected final InternalQName destName;
protected NodeData restored;
protected final SessionImpl userSession;
protected final SessionChangesLog changes;
/**
* Node paths for updating instead of deleting.
*/
protected final Set<QPath> updatingPath = new HashSet<QPath>();
/**
* Usecase of Workspace.restore(Version[], boolean), for not existing
* versionable nodes in the target workspace.
*/
protected final SessionChangesLog delegatedChanges;
private NodeTypeDataManager nodeTypeDataManager;
protected class NodeDataContext
{
private final NodeData node;
private final boolean existing;
protected NodeDataContext(NodeData node)
{
this.node = node;
this.existing = false;
}
protected NodeDataContext(NodeData node, boolean existing)
{
this.node = node;
this.existing = existing;
}
protected NodeData getNode()
{
return this.node;
}
public boolean isExisting()
{
return existing;
}
}
/**
* Prepare item states for removing subtree before restoring. Accordingly to JCR specification
* some nodes will remain and not be deleted. For this case visitor skips such nodes and stores
* its paths. For nodes with OnParentVersion attribute IGNORE ItemDataCopyIgnoredVisitor will be used
* to copy children/properties to the restored node.
*/
protected class RemoveVisitor extends ItemDataRemoveVisitor
{
/**
* The stack. In the top it contains a parent node.
*/
protected Stack<NodeData> parents = new Stack<NodeData>();
/**
* Last met node path during traversing which will remain during restore and not be deleted.
*/
private QPath remainedNode;
/**
* Node path for updating instead of deleting. All ancestors of remained nodes
* not be deleted since updating will be applied later.
*/
private final Set<QPath> updatingPath = new HashSet<QPath>();
/**
* RemoveVisitor constructor.
*
* @throws RepositoryException
*/
RemoveVisitor() throws RepositoryException
{
super(userSession.getTransientNodesManager(), null, nodeTypeDataManager, userSession.getAccessManager(),
userSession.getUserState());
}
/**
* {@inheritDoc}
*/
@Override
protected void validateReferential(NodeData node) throws RepositoryException
{
// no REFERENCE validation here
}
/**
* {@inheritDoc}
*/
@Override
protected void entering(NodeData node, int level) throws RepositoryException
{
if (level == 0)
{
removedRoot = node;
}
parents.push(node);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(NodeData node) throws RepositoryException
{
if (currentLevel > 0)
{
NodeData parent = parents.peek();
int onParentVersion =
nodeTypeDataManager.getChildNodeDefinition(node.getQPath().getName(), node.getPrimaryTypeName(),
parent.getPrimaryTypeName(), parent.getMixinTypeNames()).getOnParentVersion();
if (onParentVersion == OnParentVersionAction.VERSION
&& nodeTypeDataManager.isNodeType(Constants.MIX_VERSIONABLE, node.getPrimaryTypeName(),
node.getMixinTypeNames()))
{
remainedNode = node.getQPath();
// node and its children will remain and not be removed
return;
}
}
super.visit(node);
}
/**
* {@inheritDoc}
*/
@Override
protected void leaving(NodeData node, int level) throws RepositoryException
{
if (validate)
{
validate(node);
}
if (remainedNode != null && remainedNode.isDescendantOf(node.getQPath()))
{
updatingPath.add(node.getQPath());
}
else
{
if (!(node instanceof TransientItemData))
{
node = (NodeData)copyItemDataDelete(node);
}
ItemState state =
new ItemState(node, ItemState.DELETED, true, ancestorToSave != null ? ancestorToSave
: removedRoot.getQPath());
itemRemovedStates.add(state);
}
parents.pop();
}
/**
* {@inheritDoc}
*/
@Override
public List<ItemState> getRemovedStates()
{
return itemRemovedStates;
}
/**
* Return nodes paths for updating.
*
* @return Set
*/
public Set<QPath> getUpdatingPath()
{
return updatingPath;
}
};
ItemDataRestoreVisitor(NodeData context, InternalQName restoringName, NodeData history, SessionImpl userSession,
boolean removeExisting) throws RepositoryException
{
this(context, restoringName, history, userSession, removeExisting, null);
}
ItemDataRestoreVisitor(NodeData context, InternalQName destName, NodeData history, SessionImpl userSession,
boolean removeExisting, SessionChangesLog delegatedChanges) throws RepositoryException
{
super(userSession.getTransientNodesManager().getTransactManager());
this.userSession = userSession;
this.changes = new SessionChangesLog(userSession);
this.context = context;
this.destName = destName;
this.history = history;
this.parents.push(new NodeDataContext(context));
this.removeExisting = removeExisting;
this.nodeTypeDataManager = userSession.getWorkspace().getNodeTypesHolder();
this.delegatedChanges = delegatedChanges;
}
private NodeData currentNode()
{
return parents.peek().getNode();
}
private NodeData pushCurrent(NodeData node)
{
return parents.push(new NodeDataContext(node)).getNode();
}
private ItemData findDelegated(String identifier)
{
if (delegatedChanges != null)
{
for (ItemState state : delegatedChanges.getAllStates())
{
if (state.getData().getIdentifier().equals(identifier))
{
return state.getData();
}
}
}
return null;
}
private ItemData findDelegated(QPath path)
{
if (delegatedChanges != null)
{
for (ItemState state : delegatedChanges.getAllStates())
{
if (state.getData().getQPath().equals(path))
{
return state.getData();
}
}
}
return null;
}
private void deleteDelegated(QPath path)
{
if (delegatedChanges != null)
{
List<ItemState> removed = new ArrayList<ItemState>();
for (ItemState state : delegatedChanges.getAllStates())
{
if (state.getData().getQPath().equals(path) || state.getData().getQPath().isDescendantOf(path))
{
removed.add(state);
}
}
for (ItemState state : removed)
{
delegatedChanges.remove(state.getData().getQPath());
}
}
}
protected void initRestoreRoot(NodeData parentData, InternalQName name, NodeData frozen) throws RepositoryException
{
// WARNING: path with index=1
QPath nodePath = QPath.makeChildPath(parentData.getQPath(), name);
if (log.isDebugEnabled())
{
log.debug("Restore: " + nodePath.getAsString() + ", removeExisting=" + removeExisting);
}
PropertyData frozenIdentifier =
(PropertyData)dataManager.getItemData(frozen, new QPathEntry(Constants.JCR_FROZENUUID, 1), ItemType.PROPERTY);
String fidentifier = null;
NodeData existing = null;
// make new node from frozen
try
{
fidentifier = new String(frozenIdentifier.getValues().get(0).getAsByteArray());
NodeData sameIdentifierNodeRestored = (NodeData)findDelegated(fidentifier);
if (sameIdentifierNodeRestored != null)
{
// already restored from delegated call, remove it as we interested in
// this version state
deleteDelegated(sameIdentifierNodeRestored.getQPath());
}
else
{
NodeData sameIdentifierNode = (NodeData)dataManager.getItemData(fidentifier);
if (sameIdentifierNode != null)
{
QPath sameIdentifierPath = sameIdentifierNode.getQPath();
if (sameIdentifierPath.makeParentPath().equals(nodePath.makeParentPath()) && // same
// parent
sameIdentifierPath.getName().equals(nodePath.getName()))
{ // same
// name
if (sameIdentifierPath.getIndex() != nodePath.getIndex())
{
// but different index, see below... fix it
nodePath = QPath.makeChildPath(parentData.getQPath(), name, sameIdentifierPath.getIndex());
}
// if it's a target node
existing = sameIdentifierNode;
// remove existed node, with validation
RemoveVisitor removeVisitor = new RemoveVisitor();
removeVisitor.visit(existing);
changes.addAll(removeVisitor.getRemovedStates());
updatingPath.addAll(removeVisitor.getUpdatingPath());
}
else if (!sameIdentifierPath.isDescendantOf(nodePath))
{
if (removeExisting)
{
final QPath restorePath = nodePath;
// remove same uuid node, with validation
class RemoveVisitor extends ItemDataRemoveVisitor
{
RemoveVisitor() throws RepositoryException
{
super(userSession.getTransientNodesManager(), null, nodeTypeDataManager, userSession
.getAccessManager(), userSession.getUserState());
}
@Override
protected boolean isRemoveDescendant(ItemData item) throws RepositoryException
{
return item.getQPath().isDescendantOf(removedRoot.getQPath())
|| item.getQPath().isDescendantOf(restorePath);
}
};
ItemDataRemoveVisitor removeVisitor = new RemoveVisitor();
removeVisitor.visit(sameIdentifierNode);
changes.addAll(removeVisitor.getRemovedStates());
}
else
{
throw new ItemExistsException("Item with the same UUID as restored node " + nodePath.getAsString()
+ " already exists and removeExisting=false. Existed "
+ userSession.getLocationFactory().createJCRPath(sameIdentifierPath).getAsString(false) + " "
+ sameIdentifierNode.getIdentifier());
}
}
}
}
}
catch (IllegalStateException e)
{
throw new RepositoryException("jcr:frozenUuid, error of data read "
+ userSession.getLocationFactory().createJCRPath(frozenIdentifier.getQPath()).getAsString(false), e);
}
catch (IOException e)
{
throw new RepositoryException("jcr:frozenUuid, error of data read "
+ userSession.getLocationFactory().createJCRPath(frozenIdentifier.getQPath()).getAsString(false), e);
}
PropertyData frozenPrimaryType =
(PropertyData)dataManager.getItemData(frozen, new QPathEntry(Constants.JCR_FROZENPRIMARYTYPE, 0),
ItemType.PROPERTY);
PropertyData frozenMixinTypes =
(PropertyData)dataManager.getItemData(frozen, new QPathEntry(Constants.JCR_FROZENMIXINTYPES, 0),
ItemType.PROPERTY);
List<AccessControlEntry> accessList =
new ArrayList<AccessControlEntry>(parentData.getACL().getPermissionEntries());
String owner = parentData.getACL().getOwner();
InternalQName[] mixins = null;
if (frozenMixinTypes != null)
{
try
{
List<ValueData> mvs = frozenMixinTypes.getValues();
mixins = new InternalQName[mvs.size()];
for (int i = 0; i < mvs.size(); i++)
{
ValueData mvd = mvs.get(i);
mixins[i] = InternalQName.parse(new String(mvd.getAsByteArray()));
if (mixins[i].equals(Constants.EXO_PRIVILEGEABLE))
{
PropertyData aclData =
(PropertyData)dataManager.getItemData(frozen, new QPathEntry(Constants.EXO_PERMISSIONS, 0),
ItemType.PROPERTY);
AccessControlList acl = new AccessControlList();
acl.removePermissions(SystemIdentity.ANY);
for (ValueData value : aclData.getValues())
{
acl.addPermissions(new String(value.getAsByteArray(), Constants.DEFAULT_ENCODING));
}
accessList = acl.getPermissionEntries();
}
else if (mixins[i].equals(Constants.EXO_OWNEABLE))
{
PropertyData ownerData =
(PropertyData)dataManager.getItemData(frozen, new QPathEntry(Constants.EXO_OWNER, 0),
ItemType.PROPERTY);
owner = new String(ownerData.getValues().get(0).getAsByteArray(), Constants.DEFAULT_ENCODING);
}
}
}
catch (IllegalNameException e)
{
throw new RepositoryException("jcr:frozenMixinTypes, error of data read "
+ userSession.getLocationFactory().createJCRPath(frozenMixinTypes.getQPath()).getAsString(false), e);
}
catch (IllegalStateException e)
{
throw new RepositoryException("jcr:frozenMixinTypes, error of data read "
+ userSession.getLocationFactory().createJCRPath(frozenMixinTypes.getQPath()).getAsString(false), e);
}
catch (IOException e)
{
throw new RepositoryException("jcr:frozenMixinTypes, error of data read "
+ userSession.getLocationFactory().createJCRPath(frozenMixinTypes.getQPath()).getAsString(false), e);
}
}
AccessControlList acl = new AccessControlList(owner, accessList);
InternalQName ptName = null;
try
{
ptName = InternalQName.parse(new String(frozenPrimaryType.getValues().get(0).getAsByteArray()));
}
catch (IllegalNameException e)
{
throw new RepositoryException("jcr:frozenPrimaryType, error of data read "
+ userSession.getLocationFactory().createJCRPath(frozenPrimaryType.getQPath()).getAsString(false), e);
}
catch (IllegalStateException e)
{
throw new RepositoryException("jcr:frozenPrimaryType, error of data read "
+ userSession.getLocationFactory().createJCRPath(frozenPrimaryType.getQPath()).getAsString(false), e);
}
catch (IOException e)
{
throw new RepositoryException("jcr:frozenPrimaryType, error of data read "
+ userSession.getLocationFactory().createJCRPath(frozenPrimaryType.getQPath()).getAsString(false), e);
}
// create restored version of the node
NodeData restoredData =
new TransientNodeData(nodePath, fidentifier, (existing != null ? existing.getPersistedVersion() : -1), ptName,
mixins == null ? new InternalQName[0] : mixins, 0, parentData.getIdentifier(), acl);
if (updatingPath.contains(nodePath))
{
changes.add(ItemState.createUpdatedState(restoredData));
}
else
{
changes.add(ItemState.createAddedState(restoredData));
}
pushCurrent(restoredData);
}
@Override
protected void entering(NodeData frozen, int level) throws RepositoryException
{
if (frozen == null)
{
return;
}
if (log.isDebugEnabled())
{
log.debug("Visit node " + frozen.getQPath().getAsString() + ", HAS NULL FROZEN NODE");
}
InternalQName qname = frozen.getQPath().getName();
if (qname.equals(Constants.JCR_FROZENNODE) && level == 0)
{
// child props/nodes will be restored
if (log.isDebugEnabled())
{
log.debug("jcr:frozenNode " + frozen.getQPath().getAsString());
}
// init destenation node
initRestoreRoot(currentNode(), destName, frozen);
restored = currentNode();
}
else if (nodeTypeDataManager.isNodeType(Constants.NT_VERSIONEDCHILD, frozen.getPrimaryTypeName()))
{
QPath cvhpPropPath = QPath.makeChildPath(frozen.getQPath(), Constants.JCR_CHILDVERSIONHISTORY);
if (log.isDebugEnabled())
{
log.debug("Versioned child node " + cvhpPropPath.getAsString());
}
VersionHistoryDataHelper childHistory = null;
try
{
String vhIdentifier =
new String(((PropertyData)dataManager.getItemData(frozen, new QPathEntry(
Constants.JCR_CHILDVERSIONHISTORY, 0), ItemType.PROPERTY)).getValues().get(0).getAsByteArray());
NodeData cHistory = null;
if ((cHistory = (NodeData)dataManager.getItemData(vhIdentifier)) == null)
{
throw new RepositoryException("Version history is not found with uuid " + vhIdentifier);
}
childHistory = new VersionHistoryDataHelper(cHistory, dataManager, nodeTypeDataManager);
}
catch (IllegalStateException e)
{
throw new RepositoryException("jcr:childVersionHistory, error of data read "
+ userSession.getLocationFactory().createJCRPath(cvhpPropPath).getAsString(false), e);
}
catch (IOException e)
{
throw new RepositoryException("jcr:childVersionHistory, error of data read "
+ userSession.getLocationFactory().createJCRPath(cvhpPropPath).getAsString(false), e);
}
String versionableIdentifier = null;
try
{
versionableIdentifier =
new String(((PropertyData)dataManager.getItemData(childHistory, new QPathEntry(
Constants.JCR_VERSIONABLEUUID, 0), ItemType.PROPERTY)).getValues().get(0).getAsByteArray());
}
catch (IOException e)
{
throw new RepositoryException("jcr:childVersionHistory, error of data read "
+ userSession.getLocationFactory().createJCRPath(cvhpPropPath).getAsString(false), e);
}
NodeData versionable = (NodeData)dataManager.getItemData(versionableIdentifier);
if (versionable != null)
{
// exists,
// On restore of VN, if the workspace currently has an already
// existing node corresponding to C’s version history and the
// removeExisting flag of the restore is set to true, then that
// instance of C becomes the child of the restored N.
if (!removeExisting)
{
throw new JCRItemExistsException("Item with the same UUID " + versionableIdentifier
+ " as versionable child node "
+ userSession.getLocationFactory().createJCRPath(versionable.getQPath()).getAsString(false)
+ " already exists and removeExisting=false", versionableIdentifier);
}
// else - leaving existed unchanged
}
else
{
// not found, gets last version (by time of creation) and restore it
NodeData lastVersionData = childHistory.getLastVersionData();
NodeData cvFrozen =
(NodeData)dataManager.getItemData(lastVersionData, new QPathEntry(Constants.JCR_FROZENNODE, 1),
ItemType.NODE);
ItemDataRestoreVisitor restoreVisitor =
new ItemDataRestoreVisitor(currentNode(), qname, childHistory, userSession, removeExisting, changes);
cvFrozen.accept(restoreVisitor);
changes.addAll(restoreVisitor.getRestoreChanges().getAllStates());
}
pushCurrent(null); // skip any childs of that node
}
else if (currentNode() != null)
{
// ordinary node for copy under nt:frozenNode
// [PN] 10.04.06 In case of COPY - copy node, otherwise we don't
// 8.2.11.3 INITIALIZE; 8.2.11.4 COMPUTE
// On restore of VN, the C stored as its child will be ignored, and the
// current C in the workspace will be left unchanged.
int action =
nodeTypeDataManager.getChildNodeDefinition(qname, frozen.getPrimaryTypeName(),
currentNode().getPrimaryTypeName(),
currentNode().getMixinTypeNames()).getOnParentVersion();
if (log.isDebugEnabled())
{
log.debug("Stored node " + frozen.getQPath().getAsString() + ", "
+ OnParentVersionAction.nameFromValue(action));
}
if (action == OnParentVersionAction.COPY || action == OnParentVersionAction.VERSION)
{
// copy
QPath restoredPath =
QPath.makeChildPath(currentNode().getQPath(), frozen.getQPath().getName(), frozen.getQPath().getIndex());
// jcr:uuid
String jcrUuid = null;
NodeData existing = null;
if (nodeTypeDataManager.isNodeType(Constants.MIX_REFERENCEABLE, frozen.getPrimaryTypeName(),
frozen.getMixinTypeNames()))
{
// copy uuid from frozen state of mix:referenceable,
// NOTE: mix:referenceable stored in frozen state with genereted ID
// (JCR_XITEM PK) as UUID must be unique,
// but jcr:uuid property containts real UUID.
QPath jcrUuidPath = QPath.makeChildPath(frozen.getQPath(), Constants.JCR_UUID);
try
{
jcrUuid =
new String(((PropertyData)dataManager.getItemData(frozen, new QPathEntry(Constants.JCR_UUID, 0),
ItemType.PROPERTY)).getValues().get(0).getAsByteArray());
}
catch (IOException e)
{
throw new RepositoryException("jcr:uuid, error of data read "
+ userSession.getLocationFactory().createJCRPath(jcrUuidPath).getAsString(false), e);
}
existing = (NodeData)dataManager.getItemData(jcrUuid);
}
else
{
// try to use existing node uuid, otherwise to generate one new
existing =
(NodeData)dataManager.getItemData(currentNode(), new QPathEntry(frozen.getQPath().getName(), frozen
.getQPath().getIndex()), ItemType.NODE);
if (existing != null)
{
jcrUuid = existing.getIdentifier();
}
else
{
jcrUuid = IdGenerator.generate();
}
}
if (existing != null && !existing.getQPath().isDescendantOf(restored.getQPath()))
{
NodeData existingDelegared = (NodeData)findDelegated(existing.getQPath());
if (existingDelegared != null)
{
// was restored by previous restore (Workspace.restore(...)), remove
// it from delegated log
deleteDelegated(existing.getQPath());
}
else
{
// exists in workspace
if (removeExisting)
{
if (changes.getItemState(existing.getIdentifier(), ItemState.DELETED) == null)
{
// remove existed node, with validation (same as for restored
// root)
RemoveVisitor removeVisitor = new RemoveVisitor();
removeVisitor.visit(existing);
changes.addAll(removeVisitor.getRemovedStates());
updatingPath.addAll(removeVisitor.getUpdatingPath());
}
}
else
{
throw new ItemExistsException("Node with the same UUID as restored child node "
+ userSession.getLocationFactory().createJCRPath(restoredPath).getAsString(false)
+ " already exists and removeExisting=false. Existed "
+ userSession.getLocationFactory().createJCRPath(existing.getQPath()).getAsString(false) + " "
+ existing.getIdentifier());
}
}
}
AccessControlList acl = currentNode().getACL();
boolean isPrivilegeable =
nodeTypeDataManager.isNodeType(Constants.EXO_PRIVILEGEABLE, frozen.getPrimaryTypeName(),
frozen.getMixinTypeNames());
boolean isOwneable =
nodeTypeDataManager.isNodeType(Constants.EXO_OWNEABLE, frozen.getPrimaryTypeName(),
frozen.getMixinTypeNames());
if (isPrivilegeable || isOwneable)
{
List<AccessControlEntry> permissionEntries = new ArrayList<AccessControlEntry>();
permissionEntries.addAll((isPrivilegeable ? frozen.getACL() : currentNode().getACL())
.getPermissionEntries());
String owner = isOwneable ? frozen.getACL().getOwner() : currentNode().getACL().getOwner();
acl = new AccessControlList(owner, permissionEntries);
}
NodeData restoredData =
new TransientNodeData(restoredPath, jcrUuid, frozen.getPersistedVersion(), frozen.getPrimaryTypeName(),
frozen.getMixinTypeNames(), frozen.getOrderNumber(), currentNode().getIdentifier(), acl);
if (updatingPath.contains(restoredPath))
{
changes.add(ItemState.createUpdatedState(restoredData));
}
else
{
changes.add(ItemState.createAddedState(restoredData));
}
pushCurrent(restoredData);
}
else if (action == OnParentVersionAction.INITIALIZE || action == OnParentVersionAction.COMPUTE)
{
// current C in the workspace will be left unchanged,
NodeData existed =
(NodeData)dataManager.getItemData(currentNode(), new QPathEntry(frozen.getQPath().getName(), 0),
ItemType.NODE);
if (existed != null)
{
// copy existed - i.e. left unchanged
ItemDataCopyVisitor copyVisitor =
new ItemDataCopyVisitor(currentNode(), frozen.getQPath().getName(), nodeTypeDataManager,
userSession.getTransientNodesManager(), userSession.getTransientNodesManager(), true);
existed.accept(copyVisitor);
changes.addAll(copyVisitor.getItemAddStates());
} // else - nothing to do, i.e. left unchanged
pushCurrent(null); // JCR-193, skip any childs of that node now
}
}
}
@Override
protected void entering(PropertyData property, int level) throws RepositoryException
{
if (currentNode() != null)
{
NodeData frozenParent = (NodeData)dataManager.getItemData(property.getParentIdentifier());
InternalQName qname = property.getQPath().getName();
if (nodeTypeDataManager.isNodeType(Constants.NT_FROZENNODE, frozenParent.getPrimaryTypeName()))
{
if (qname.equals(Constants.JCR_FROZENPRIMARYTYPE))
{
qname = Constants.JCR_PRIMARYTYPE;
}
else if (qname.equals(Constants.JCR_FROZENUUID))
{
qname = Constants.JCR_UUID;
}
else if (qname.equals(Constants.JCR_FROZENMIXINTYPES))
{
qname = Constants.JCR_MIXINTYPES;
}
else if (qname.equals(Constants.JCR_PRIMARYTYPE) || qname.equals(Constants.JCR_UUID)
|| qname.equals(Constants.JCR_MIXINTYPES))
{
// skip these props, as they are a nt:frozenNode special props
return;
}
}
int action =
nodeTypeDataManager
.getPropertyDefinitions(qname, currentNode().getPrimaryTypeName(), currentNode().getMixinTypeNames())
.getAnyDefinition().getOnParentVersion();
if (log.isDebugEnabled())
{
log.debug("Visit property " + property.getQPath().getAsString() + " "
+ currentNode().getQPath().getAsString() + " " + OnParentVersionAction.nameFromValue(action));
}
if (action == OnParentVersionAction.COPY || action == OnParentVersionAction.VERSION
|| action == OnParentVersionAction.INITIALIZE || action == OnParentVersionAction.COMPUTE)
{
// In case of COPY, VERSION - copy property
PropertyData tagetProperty = null;
if (qname.equals(Constants.JCR_PREDECESSORS))
{
tagetProperty =
TransientPropertyData.createPropertyData(currentNode(), qname, property.getType(),
property.isMultiValued(), new ArrayList<ValueData>());
}
else
{
tagetProperty =
TransientPropertyData.createPropertyData(currentNode(), qname, property.getType(),
property.isMultiValued(), copyValues(property));
}
changes.add(ItemState.createAddedState(tagetProperty));
}
else if (log.isDebugEnabled())
{
// else - nothing to do, i.e. left unchanged
log.debug("Visit property " + property.getQPath().getAsString() + " HAS "
+ OnParentVersionAction.nameFromValue(action) + " action");
}
}
else if (log.isDebugEnabled())
{
log.debug("Visit property " + property.getQPath().getAsString()
+ " HAS NULL PARENT. Restore of this property is impossible.");
}
}
@Override
protected void leaving(NodeData frozen, int level) throws RepositoryException
{
InternalQName qname = frozen.getQPath().getName();
if (qname.equals(Constants.JCR_FROZENNODE) && level == 0)
{
if (log.isDebugEnabled())
{
log.debug("leaving jcr:frozenNode " + frozen.getQPath().getAsString());
}
// post init of a restored node
PropertyData baseVersion =
TransientPropertyData.createPropertyData(restored, Constants.JCR_BASEVERSION, PropertyType.REFERENCE,
false, new TransientValueData(frozen.getParentIdentifier()));
PropertyData isCheckedOut =
TransientPropertyData.createPropertyData(restored, Constants.JCR_ISCHECKEDOUT, PropertyType.BOOLEAN, false,
new TransientValueData(false));
NodeData existing = (NodeData)dataManager.getItemData(restored.getIdentifier());
if (existing != null && !existing.getQPath().isDescendantOf(Constants.JCR_VERSION_STORAGE_PATH))
{
// copy childs/properties with OnParentVersionAction.IGNORE to the
// restored node
ItemDataCopyIgnoredVisitor copyIgnoredVisitor =
new ItemDataCopyIgnoredVisitor((NodeData)dataManager.getItemData(restored.getParentIdentifier()),
restored.getQPath().getName(), nodeTypeDataManager, userSession.getTransientNodesManager(), changes);
existing.accept(copyIgnoredVisitor);
changes.addAll(copyIgnoredVisitor.getItemAddStates());
}
changes.add(ItemState.createAddedState(baseVersion));
changes.add(ItemState.createAddedState(isCheckedOut));
}
if (parents.size() <= 0)
{
log.error("Empty parents stack");
}
parents.pop();
}
@Override
protected void leaving(PropertyData property, int level) throws RepositoryException
{
}
public SessionChangesLog getRestoreChanges()
{
return changes;
}
public NodeData getRestoreRoot()
{
return restored;
}
}