/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.core.version;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.PropertyImpl;
import org.apache.jackrabbit.core.NodeId;
import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
import org.apache.jackrabbit.core.state.ItemStateException;
import org.apache.jackrabbit.core.state.NodeState;
import org.apache.jackrabbit.core.state.PropertyState;
import org.apache.jackrabbit.core.value.InternalValue;
import org.apache.jackrabbit.name.QName;
import org.apache.jackrabbit.uuid.UUID;
import javax.jcr.NodeIterator;
import javax.jcr.PropertyIterator;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.nodetype.NodeType;
import javax.jcr.version.OnParentVersionAction;
import javax.jcr.version.VersionException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Implements a <code>InternalFrozenNode</code>
*/
class InternalFrozenNodeImpl extends InternalFreezeImpl
implements InternalFrozenNode {
/**
* checkin mode version.
*/
private static final int MODE_VERSION = 0;
/**
* checkin mode copy. specifies, that the items are always copied.
*/
private static final int MODE_COPY = 1;
/**
* mode flag specifies, that the mode should be recursed. otherwise i
* will be redetermined by the opv.
*/
private static final int MODE_COPY_RECURSIVE = 3;
/**
* the list of frozen properties
*/
private PropertyState[] frozenProperties;
/**
* the frozen child nodes
*/
private InternalFreeze[] frozenNodes = null;
/**
* the frozen uuid of the original node
*/
private UUID frozenUUID = null;
/**
* the frozen primary type of the orginal node
*/
private QName frozenPrimaryType = null;
/**
* the frozen list of mixin types of the original node
*/
private QName[] frozenMixinTypes = null;
/**
* Creates a new frozen node based on the given persistance node.
*
* @param node
* @throws javax.jcr.RepositoryException
*/
public InternalFrozenNodeImpl(AbstractVersionManager vMgr, NodeStateEx node,
InternalVersionItem parent)
throws RepositoryException {
super(vMgr, node, parent);
// init the frozen properties
PropertyState[] props;
try {
props = node.getProperties();
} catch (ItemStateException e) {
throw new RepositoryException(e);
}
List propList = new ArrayList();
for (int i = 0; i < props.length; i++) {
PropertyState prop = props[i];
if (prop.getName().equals(QName.JCR_FROZENUUID)) {
// special property
frozenUUID = UUID.fromString(node.getPropertyValue(QName.JCR_FROZENUUID).internalValue().toString());
} else if (prop.getName().equals(QName.JCR_FROZENPRIMARYTYPE)) {
// special property
frozenPrimaryType = (QName) node.getPropertyValue(QName.JCR_FROZENPRIMARYTYPE).internalValue();
} else if (prop.getName().equals(QName.JCR_FROZENMIXINTYPES)) {
// special property
InternalValue[] values = node.getPropertyValues(QName.JCR_FROZENMIXINTYPES);
if (values == null) {
frozenMixinTypes = new QName[0];
} else {
frozenMixinTypes = new QName[values.length];
for (int j = 0; j < values.length; j++) {
frozenMixinTypes[j] = (QName) values[j].internalValue();
}
}
} else if (prop.getName().equals(QName.JCR_PRIMARYTYPE)) {
// ignore
} else if (prop.getName().equals(QName.JCR_UUID)) {
// ignore
} else {
propList.add(prop);
}
}
frozenProperties = (PropertyState[]) propList.toArray(new PropertyState[propList.size()]);
// do some checks
if (frozenMixinTypes == null) {
frozenMixinTypes = new QName[0];
}
if (frozenPrimaryType == null) {
throw new RepositoryException("Illegal frozen node. Must have 'frozenPrimaryType'");
}
}
/**
* {@inheritDoc}
*/
public QName getName() {
return node.getName();
}
/**
* {@inheritDoc}
*/
public NodeId getId() {
return node.getNodeId();
}
/**
* {@inheritDoc}
*/
public synchronized InternalFreeze[] getFrozenChildNodes()
throws VersionException {
if (frozenNodes == null) {
try {
// maybe add iterator?
List entries = node.getState().getChildNodeEntries();
frozenNodes = new InternalFreeze[entries.size()];
Iterator iter = entries.iterator();
int i = 0;
while (iter.hasNext()) {
NodeState.ChildNodeEntry entry =
(NodeState.ChildNodeEntry) iter.next();
frozenNodes[i++] = (InternalFreeze) vMgr.getItem(entry.getId());
}
} catch (RepositoryException e) {
throw new VersionException("Unable to retrieve frozen child nodes", e);
}
}
return frozenNodes;
}
/**
* {@inheritDoc}
*/
public boolean hasFrozenHistory(UUID uuid) {
try {
NodeId id = new NodeId(uuid);
InternalFreeze[] frozen = getFrozenChildNodes();
for (int i=0; i<frozen.length; i++) {
if (frozen[i] instanceof InternalFrozenVersionHistory &&
((InternalFrozenVersionHistory) frozen[i])
.getVersionHistoryId().equals(id)) {
return true;
}
}
} catch (RepositoryException e) {
// ignore
}
return false;
}
/**
* {@inheritDoc}
*/
public PropertyState[] getFrozenProperties() {
return frozenProperties;
}
/**
* {@inheritDoc}
*/
public UUID getFrozenUUID() {
return frozenUUID;
}
/**
* {@inheritDoc}
*/
public QName getFrozenPrimaryType() {
return frozenPrimaryType;
}
/**
* {@inheritDoc}
*/
public QName[] getFrozenMixinTypes() {
return frozenMixinTypes;
}
/**
* Checks-in a <code>src</code> node. It creates a new child node of
* <code>parent</code> with the given <code>name</code> and adds the
* source nodes properties according to their OPV value to the
* list of frozen properties. It creates frozen child nodes for each child
* node of <code>src</code> according to its OPV value.
*
* @param parent
* @param name
* @param src
* @return
* @throws RepositoryException
*/
protected static NodeStateEx checkin(NodeStateEx parent, QName name,
NodeImpl src)
throws RepositoryException {
return checkin(parent, name, src, MODE_VERSION);
}
/**
* Checks-in a <code>src</code> node. It creates a new child node of
* <code>parent</code> with the given <code>name</code> and adds the
* source nodes properties according to their OPV value to the
* list of frozen properties. It creates frozen child nodes for each child
* node of <code>src</code> according to its OPV value.
*
* @param parent
* @param name
* @param src
* @return
* @throws RepositoryException
*/
private static NodeStateEx checkin(NodeStateEx parent, QName name,
NodeImpl src, int mode)
throws RepositoryException {
// create new node
NodeStateEx node = parent.addNode(name, QName.NT_FROZENNODE, null, true);
// initialize the internal properties
node.setPropertyValue(QName.JCR_FROZENUUID,
InternalValue.create(src.internalGetUUID().toString()));
node.setPropertyValue(QName.JCR_FROZENPRIMARYTYPE,
InternalValue.create(((NodeTypeImpl) src.getPrimaryNodeType()).getQName()));
if (src.hasProperty(QName.JCR_MIXINTYPES)) {
NodeType[] mixins = src.getMixinNodeTypes();
InternalValue[] ivalues = new InternalValue[mixins.length];
for (int i = 0; i < mixins.length; i++) {
ivalues[i] = InternalValue.create(((NodeTypeImpl) mixins[i]).getQName());
}
node.setPropertyValues(QName.JCR_FROZENMIXINTYPES, PropertyType.NAME, ivalues);
}
// add the properties
PropertyIterator piter = src.getProperties();
while (piter.hasNext()) {
PropertyImpl prop = (PropertyImpl) piter.nextProperty();
int opv;
if ((mode & MODE_COPY) > 0) {
opv = OnParentVersionAction.COPY;
} else {
opv = prop.getDefinition().getOnParentVersion();
}
switch (opv) {
case OnParentVersionAction.ABORT:
parent.reload();
throw new VersionException("Checkin aborted due to OPV in " + prop.safeGetJCRPath());
case OnParentVersionAction.COMPUTE:
case OnParentVersionAction.IGNORE:
case OnParentVersionAction.INITIALIZE:
break;
case OnParentVersionAction.VERSION:
case OnParentVersionAction.COPY:
// ignore frozen properties
if (!prop.getQName().equals(QName.JCR_PRIMARYTYPE)
&& !prop.getQName().equals(QName.JCR_MIXINTYPES)
&& !prop.getQName().equals(QName.JCR_UUID)) {
node.copyFrom(prop);
}
break;
}
}
// add the frozen children and histories
NodeIterator niter = src.getNodes();
while (niter.hasNext()) {
NodeImpl child = (NodeImpl) niter.nextNode();
int opv;
if ((mode & MODE_COPY_RECURSIVE) > 0) {
opv = OnParentVersionAction.COPY;
} else {
opv = child.getDefinition().getOnParentVersion();
}
switch (opv) {
case OnParentVersionAction.ABORT:
throw new VersionException("Checkin aborted due to OPV in " + child.safeGetJCRPath());
case OnParentVersionAction.COMPUTE:
case OnParentVersionAction.IGNORE:
case OnParentVersionAction.INITIALIZE:
break;
case OnParentVersionAction.VERSION:
if (child.isNodeType(QName.MIX_VERSIONABLE)) {
// create frozen versionable child
NodeStateEx newChild = node.addNode(child.getQName(), QName.NT_VERSIONEDCHILD, null, false);
newChild.setPropertyValue(QName.JCR_CHILDVERSIONHISTORY,
InternalValue.create(new UUID(child.getVersionHistory().getUUID())));
/*
newChild.setPropertyValue(JCR_BASEVERSION,
InternalValue.create(child.getBaseVersion().getUUID()));
*/
break;
}
// else copy but do not recurse
checkin(node, child.getQName(), child, MODE_COPY);
break;
case OnParentVersionAction.COPY:
checkin(node, child.getQName(), child, MODE_COPY_RECURSIVE);
break;
}
}
return node;
}
}