/* * 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 java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.HashSet; import java.util.Collections; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.version.OnParentVersionAction; import javax.jcr.version.VersionException; import org.apache.jackrabbit.core.id.NodeId; import org.apache.jackrabbit.core.state.ChildNodeEntry; import org.apache.jackrabbit.core.state.ItemStateException; import org.apache.jackrabbit.core.state.PropertyState; import org.apache.jackrabbit.core.value.InternalValue; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.commons.name.NameConstants; /** * Implements a <code>InternalFrozenNode</code> */ class InternalFrozenNodeImpl extends InternalFreezeImpl implements InternalFrozenNode { /** * the list of frozen properties */ private PropertyState[] frozenProperties; /** * the frozen id of the original node */ private NodeId frozenUUID = null; /** * the frozen primary type of the original node */ private Name frozenPrimaryType = null; /** * the frozen list of mixin types of the original node */ private Set<Name> frozenMixinTypes = null; /** * Creates a new frozen node based on the given persistence node. * * @param vMgr version manager * @param node underlying node * @param parent parent item * @throws RepositoryException if an error occurs */ public InternalFrozenNodeImpl(InternalVersionManagerBase 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<PropertyState> propList = new ArrayList<PropertyState>(); Set<Name> mixins = new HashSet<Name>(); for (PropertyState prop : props) { if (prop.getName().equals(NameConstants.JCR_FROZENUUID)) { // special property InternalValue value = node.getPropertyValue(NameConstants.JCR_FROZENUUID); // JCR-1803: The value should be a STRING, but older Jackrabbit // versions (< 1.1, see JCR-487) used REFERENCE values. Since // we do not automatically upgrade old content, we need to be // ready to handle both types of values here. if (value.getType() == PropertyType.STRING) { frozenUUID = new NodeId(value.getString()); } else { frozenUUID = value.getNodeId(); } } else if (prop.getName().equals(NameConstants.JCR_FROZENPRIMARYTYPE)) { // special property frozenPrimaryType = node.getPropertyValue(NameConstants.JCR_FROZENPRIMARYTYPE).getName(); } else if (prop.getName().equals(NameConstants.JCR_FROZENMIXINTYPES)) { // special property InternalValue[] values = node.getPropertyValues(NameConstants.JCR_FROZENMIXINTYPES); if (values != null) { for (InternalValue value : values) { mixins.add(value.getName()); } } } else if (!prop.getName().equals(NameConstants.JCR_PRIMARYTYPE) && !prop.getName().equals(NameConstants.JCR_UUID)) { propList.add(prop); } } frozenProperties = propList.toArray(new PropertyState[propList.size()]); frozenMixinTypes = Collections.unmodifiableSet(mixins); // do some checks if (frozenPrimaryType == null) { throw new RepositoryException("Illegal frozen node. Must have 'frozenPrimaryType'"); } } /** * {@inheritDoc} */ public Name getName() { return node.getName(); } /** * {@inheritDoc} */ @Override public NodeId getId() { return node.getNodeId(); } /** * {@inheritDoc} */ public List<ChildNodeEntry> getFrozenChildNodes() throws VersionException { return node.getState().getChildNodeEntries(); } /** * {@inheritDoc} */ public boolean hasFrozenChildNode(Name name, int idx) { return node.getState().hasChildNodeEntry(name, idx); } /** * {@inheritDoc} */ public InternalFreeze getFrozenChildNode(Name name, int idx) throws RepositoryException { ChildNodeEntry e = node.getState().getChildNodeEntry(name, idx); return e == null ? null : (InternalFreeze) vMgr.getItem(e.getId()); } /** * {@inheritDoc} */ public PropertyState[] getFrozenProperties() { return frozenProperties; } /** * {@inheritDoc} */ public NodeId getFrozenId() { return frozenUUID; } /** * {@inheritDoc} */ public Name getFrozenPrimaryType() { return frozenPrimaryType; } /** * {@inheritDoc} */ public Set<Name> 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 destination parent * @param name new node name * @param src source node state * @return the node node state * @throws RepositoryException if an error occurs */ protected static NodeStateEx checkin(NodeStateEx parent, Name name, NodeStateEx src) throws RepositoryException { try { return checkin(parent, name, src, false); } catch (ItemStateException e) { throw new RepositoryException(e); } } /** * 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 destination parent * @param name new node name * @param src source node state * @param forceCopy if <code>true</code> the OPV is ignored and a COPY is performed * @return the nde node state * @throws RepositoryException if an error occurs * @throws ItemStateException if an error during reading the items occurs */ private static NodeStateEx checkin(NodeStateEx parent, Name name, NodeStateEx src, boolean forceCopy) throws RepositoryException, ItemStateException { // create new node NodeStateEx node = parent.addNode(name, NameConstants.NT_FROZENNODE, null, true); // initialize the internal properties node.setPropertyValue(NameConstants.JCR_FROZENUUID, InternalValue.create(src.getNodeId().toString())); node.setPropertyValue(NameConstants.JCR_FROZENPRIMARYTYPE, InternalValue.create(src.getState().getNodeTypeName())); if (src.hasProperty(NameConstants.JCR_MIXINTYPES)) { node.setPropertyValues(NameConstants.JCR_FROZENMIXINTYPES, PropertyType.NAME, src.getPropertyValues(NameConstants.JCR_MIXINTYPES)); } // add the properties for (PropertyState prop: src.getProperties()) { int opv; if (forceCopy) { opv = OnParentVersionAction.COPY; } else { opv = src.getDefinition(prop).getOnParentVersion(); } Name propName = prop.getName(); if (opv == OnParentVersionAction.ABORT) { parent.reload(); throw new VersionException("Checkin aborted due to OPV abort in " + propName); } else if (opv == OnParentVersionAction.VERSION || opv == OnParentVersionAction.COPY) { // ignore frozen properties if (!propName.equals(NameConstants.JCR_PRIMARYTYPE) && !propName.equals(NameConstants.JCR_MIXINTYPES) && !propName.equals(NameConstants.JCR_UUID) // JCR-3635: should never occur in normal content... && !propName.equals(NameConstants.JCR_FROZENPRIMARYTYPE) && !propName.equals(NameConstants.JCR_FROZENMIXINTYPES) && !propName.equals(NameConstants.JCR_FROZENUUID)) { node.copyFrom(prop); } } } // add the frozen children and histories boolean isFull = src.getEffectiveNodeType().includesNodeType(NameConstants.MIX_VERSIONABLE); for (NodeStateEx child: src.getChildNodes()) { int opv; if (forceCopy) { opv = OnParentVersionAction.COPY; } else { opv = child.getDefinition().getOnParentVersion(); } if (opv == OnParentVersionAction.ABORT) { throw new VersionException("Checkin aborted due to OPV in " + child); } else if (opv == OnParentVersionAction.VERSION) { if (isFull && child.getEffectiveNodeType().includesNodeType(NameConstants.MIX_VERSIONABLE)) { // create frozen versionable child NodeId histId = child.getPropertyValue(NameConstants.JCR_VERSIONHISTORY).getNodeId(); NodeStateEx newChild = node.addNode(child.getName(), NameConstants.NT_VERSIONEDCHILD, null, false); newChild.setPropertyValue( NameConstants.JCR_CHILDVERSIONHISTORY, InternalValue.create(histId)); } else { // else copy checkin(node, child.getName(), child, true); } } else if (opv == OnParentVersionAction.COPY) { checkin(node, child.getName(), child, true); } } return node; } }