Package org.apache.jackrabbit.jcr2spi.state

Source Code of org.apache.jackrabbit.jcr2spi.state.WorkspaceItemStateFactory

/*
* 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.jcr2spi.state;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.jackrabbit.jcr2spi.nodetype.ItemDefinitionProvider;
import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
import org.apache.jackrabbit.jcr2spi.hierarchy.HierarchyEntry;
import org.apache.jackrabbit.spi.commons.name.NameConstants;
import org.apache.jackrabbit.spi.NodeId;
import org.apache.jackrabbit.spi.PropertyId;
import org.apache.jackrabbit.spi.NodeInfo;
import org.apache.jackrabbit.spi.PropertyInfo;
import org.apache.jackrabbit.spi.SessionInfo;
import org.apache.jackrabbit.spi.RepositoryService;
import org.apache.jackrabbit.spi.ItemInfo;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.Path;

import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.ItemExistsException;
import javax.jcr.ItemNotFoundException;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;

/**
* <code>WorkspaceItemStateFactory</code>...
*/
public class WorkspaceItemStateFactory extends AbstractItemStateFactory implements ItemStateFactory {

    private static Logger log = LoggerFactory.getLogger(WorkspaceItemStateFactory.class);

    private final RepositoryService service;
    private final SessionInfo sessionInfo;
    private final ItemDefinitionProvider definitionProvider;

    public WorkspaceItemStateFactory(RepositoryService service, SessionInfo sessionInfo,
                                     ItemDefinitionProvider definitionProvider) {
        this.service = service;
        this.sessionInfo = sessionInfo;
        this.definitionProvider = definitionProvider;
    }

    /**
     * @inheritDoc
     * @see ItemStateFactory#createRootState(NodeEntry)
     */
    public NodeState createRootState(NodeEntry entry) throws ItemNotFoundException, RepositoryException {
        return createNodeState(service.getRootId(sessionInfo), entry);
    }

    /**
     * Creates the node with information retrieved from the
     * <code>RepositoryService</code>.
     *
     * @inheritDoc
     * @see ItemStateFactory#createNodeState(NodeId,NodeEntry)
     */
    public NodeState createNodeState(NodeId nodeId, NodeEntry entry)
            throws ItemNotFoundException, RepositoryException {
        // build new node state from server information
        try {
            NodeState nodeState;
            if (entry.getStatus() == Status.INVALIDATED) {
                // simple reload -> don't use batch-read
                NodeInfo nInfo = service.getNodeInfo(sessionInfo, nodeId);
                nodeState = createItemStates(nodeId, Collections.singletonList(nInfo).iterator(), entry, false);
            } else {
                Iterator infos = service.getItemInfos(sessionInfo, nodeId);
                nodeState = createItemStates(nodeId, infos, entry, false);
            }
            if (nodeState == null) {
                throw new ItemNotFoundException("HierarchyEntry does not belong to any existing ItemInfo.");
            }
            return nodeState;
        } catch (PathNotFoundException e) {
            throw new ItemNotFoundException(e.getMessage(), e);
        }
    }

    /**
     * @inheritDoc
     * @see ItemStateFactory#createDeepNodeState(NodeId,NodeEntry)
     */
    public NodeState createDeepNodeState(NodeId nodeId, NodeEntry anyParent) throws ItemNotFoundException, RepositoryException {
        try {
            Iterator infos = service.getItemInfos(sessionInfo, nodeId);
            return createItemStates(nodeId, infos, anyParent, true);
        } catch (PathNotFoundException e) {
            throw new ItemNotFoundException(e.getMessage(), e);
        }
    }

    /**
     * Creates the PropertyState with information retrieved from the
     * <code>RepositoryService</code>.
     *
     * @inheritDoc
     * @see ItemStateFactory#createPropertyState(PropertyId,PropertyEntry)
     */
    public PropertyState createPropertyState(PropertyId propertyId,
                                             PropertyEntry entry)
            throws ItemNotFoundException, RepositoryException {
        try {
            PropertyInfo info = service.getPropertyInfo(sessionInfo, propertyId);
            assertMatchingPath(info, entry);
            return createPropertyState(info, entry);
        } catch (PathNotFoundException e) {
            throw new ItemNotFoundException(e.getMessage());
        }
    }

    /**
     * @inheritDoc
     * @see ItemStateFactory#createDeepPropertyState(PropertyId,NodeEntry)
     */
    public PropertyState createDeepPropertyState(PropertyId propertyId, NodeEntry anyParent) throws ItemNotFoundException, RepositoryException {
        try {
            PropertyInfo info = service.getPropertyInfo(sessionInfo, propertyId);
            return createDeepPropertyState(info, anyParent);
        } catch (PathNotFoundException e) {
            throw new ItemNotFoundException(e.getMessage());
        }
    }

    /**
     * @inheritDoc
     * @see ItemStateFactory#getChildNodeInfos(NodeId)
     * @param nodeId
     */
    public Iterator getChildNodeInfos(NodeId nodeId)
            throws ItemNotFoundException, RepositoryException {
        return service.getChildInfos(sessionInfo, nodeId);
    }

    /**
     * @inheritDoc
     * @see ItemStateFactory#getNodeReferences(NodeState)
     * @param nodeState
     */
    public PropertyId[] getNodeReferences(NodeState nodeState) {
        NodeEntry entry = nodeState.getNodeEntry();
        // shortcut
        if (entry.getUniqueID() == null
                || !entry.hasPropertyEntry(NameConstants.JCR_UUID)) {
            // for sure not referenceable
            return new PropertyId[0];
        }

        // nodestate has a unique ID and is potentially mix:referenceable
        // => try to retrieve references
        try {
            NodeInfo nInfo = service.getNodeInfo(sessionInfo, entry.getWorkspaceId());
            return nInfo.getReferences();
        } catch (RepositoryException e) {
            log.debug("Unable to determine references to {}", nodeState);
            return new PropertyId[0];
        }
    }

    //------------------------------------------------------------< private >---
    /**
     *
     * @param nodeId
     * @param itemInfos
     * @param entry
     * @return
     * @throws ItemNotFoundException
     * @throws RepositoryException
     */
    private synchronized NodeState createItemStates(NodeId nodeId,
                                                    Iterator itemInfos,
                                                    NodeEntry entry,
                                                    boolean isDeep)
            throws ItemNotFoundException, RepositoryException {
        NodeState nodeState;
        // first entry in the iterator is the originally requested Node.
        if (itemInfos.hasNext()) {
            NodeInfo first = (NodeInfo) itemInfos.next();
            if (isDeep) {
                // for a deep state, the hierarchy entry does not correspond to
                // the given NodeEntry -> retrieve NodeState before executing
                // validation check.
                nodeState = createDeepNodeState(first, entry);
                assertMatchingPath(first, nodeState.getNodeEntry());
            } else {
                // 'isDeep' == false -> the given NodeEntry must match to the
                // first ItemInfo retrieved from the iterator.
                assertMatchingPath(first, entry);
                nodeState = createNodeState(first, entry);
            }
        } else {
            // empty iterator
            throw new ItemNotFoundException("Node with id " + nodeId + " could not be found.");
        }

        // deal with all additional ItemInfos that may be present.
        NodeEntry parentEntry = nodeState.getNodeEntry();
        if (parentEntry.getStatus() != Status.INVALIDATED) {
            while (itemInfos.hasNext()) {
                ItemInfo info = (ItemInfo) itemInfos.next();
                if (info.denotesNode()) {
                    createDeepNodeState((NodeInfo) info, parentEntry);
                } else {
                    createDeepPropertyState((PropertyInfo) info, parentEntry);
                }
            }
        }
        return nodeState;
    }

    /**
     * Creates the node with information retrieved from <code>info</code>.
     *
     * @param info the <code>NodeInfo</code> to use to create the <code>NodeState</code>.
     * @param entry
     * @return the new <code>NodeState</code>.
     * @throws ItemNotFoundException
     * @throws RepositoryException
     */
    private NodeState createNodeState(NodeInfo info, NodeEntry entry) throws ItemNotFoundException, RepositoryException {
        // make sure the entry has the correct ItemId
        // this make not be the case, if the hierachy has not been completely
        // resolved yet -> if uniqueID is present, set it on this entry or on
        // the appropriate parent entry
        String uniqueID = info.getId().getUniqueID();
        Path path = info.getId().getPath();
        if (path == null) {
            entry.setUniqueID(uniqueID);
        } else if (uniqueID != null) {
            // uniqueID that applies to a parent NodeEntry -> get parentEntry
            NodeEntry parent = getAncestor(entry, path.getLength());
            parent.setUniqueID(uniqueID);
        }

        // now build the nodestate itself
        NodeState state = new NodeState(entry, info, this, definitionProvider);
        state.setMixinTypeNames(info.getMixins());

        // update NodeEntry from the information present in the NodeInfo (prop entries)
        List propNames = new ArrayList();
        for (Iterator it = info.getPropertyIds(); it.hasNext(); ) {
            PropertyId pId = (PropertyId) it.next();
            Name propertyName = pId.getName();
            propNames.add(propertyName);
        }
        try {
            entry.addPropertyEntries(propNames);
        } catch (ItemExistsException e) {
            // should not get here
            log.warn("Internal error", e);
        }

        notifyCreated(state);
        return state;
    }

    /**
     * Creates the property with information retrieved from <code>info</code>.
     *
     * @param info   the <code>PropertyInfo</code> to use to create the
     *               <code>PropertyState</code>.
     * @param entry
     * @return the new <code>PropertyState</code>.
     */
    private PropertyState createPropertyState(PropertyInfo info, PropertyEntry entry) {
        // make sure uuid part of id is correct
        String uniqueID = info.getId().getUniqueID();
        if (uniqueID != null) {
            // uniqueID always applies to a parent NodeEntry -> get parentEntry
            NodeEntry parent = getAncestor(entry, info.getId().getPath().getLength());
            parent.setUniqueID(uniqueID);
        }

        // build the PropertyState
        PropertyState state = new PropertyState(entry, info, this, definitionProvider);

        notifyCreated(state);
        return state;
    }

    /**
     *
     * @param info
     * @param anyParent
     * @return
     * @throws RepositoryException
     */
    private NodeState createDeepNodeState(NodeInfo info, NodeEntry anyParent) throws RepositoryException {
        try {
            // node for nodeId exists -> build missing entries in hierarchy
            // Note, that the path contained in NodeId does not reveal which
            // entries are missing -> calculate relative path.
            Path anyParentPath = anyParent.getPath();
            Path relPath = anyParentPath.computeRelativePath(info.getPath());
            Path.Element[] missingElems = relPath.getElements();

            NodeEntry entry = anyParent;
            for (int i = 0; i < missingElems.length; i++) {
                Name name = missingElems[i].getName();
                int index = missingElems[i].getNormalizedIndex();
                entry = createIntermediateNodeEntry(entry, name, index);
            }
            if (entry == anyParent) {
                throw new RepositoryException("Internal error while getting deep itemState");
            }
            return createNodeState(info, entry);
        } catch (PathNotFoundException e) {
            throw new ItemNotFoundException(e.getMessage(), e);
        }
    }

    /**
     *
     * @param info
     * @param anyParent
     * @return
     * @throws RepositoryException
     */
    private PropertyState createDeepPropertyState(PropertyInfo info, NodeEntry anyParent) throws RepositoryException {
        try {
            // prop for propertyId exists -> build missing entries in hierarchy
            // Note, that the path contained in PropertyId does not reveal which
            // entries are missing -> calculate relative path.
            Path anyParentPath = anyParent.getPath();
            Path relPath = anyParentPath.computeRelativePath(info.getPath());
            Path.Element[] missingElems = relPath.getElements();
            NodeEntry entry = anyParent;
            int i = 0;
            // NodeEntries except for the very last 'missingElem'
            while (i < missingElems.length - 1) {
                Name name = missingElems[i].getName();
                int index = missingElems[i].getNormalizedIndex();
                entry = createIntermediateNodeEntry(entry, name, index);
                i++;
            }
            // create PropertyEntry for the last element if not existing yet
            Name propName = missingElems[i].getName();
            PropertyEntry propEntry = entry.getPropertyEntry(propName);
            if (propEntry == null) {
                propEntry = entry.addPropertyEntry(propName);
            }
            return createPropertyState(info, propEntry);
        } catch (PathNotFoundException e) {
            throw new ItemNotFoundException(e.getMessage());
        }
    }

    /**
     *
     * @param parentEntry
     * @param name
     * @param index
     * @return
     * @throws RepositoryException
     */
    private static NodeEntry createIntermediateNodeEntry(NodeEntry parentEntry, Name name, int index) throws RepositoryException {
        NodeEntry entry;
        if (parentEntry.hasNodeEntry(name, index)) {
            entry = parentEntry.getNodeEntry(name, index);
        } else {
            entry = parentEntry.addNodeEntry(name, null, index);
        }
        return entry;
    }

    /**
     * Validation check: Path of the given ItemInfo must match to the Path of
     * the HierarchyEntry. This is required for Items that are identified by
     * a uniqueID that may move within the hierarchy upon restore or clone.
     *
     * @param info
     * @param entry
     * @throws ItemNotFoundException
     * @throws RepositoryException
     */
    private static void assertMatchingPath(ItemInfo info, HierarchyEntry entry)
            throws ItemNotFoundException, RepositoryException {
        Path infoPath = info.getPath();
        if (!infoPath.equals(entry.getWorkspacePath())) {
            // TODO: handle external move of nodes (parents) identified by uniqueID
            throw new ItemNotFoundException("HierarchyEntry does not belong the given ItemInfo.");
        }
    }

    /**
     *
     * @param entry
     * @param degree
     * @return
     */
    private static NodeEntry getAncestor(HierarchyEntry entry, int degree) {
        NodeEntry parent = entry.getParent();
        degree--;
        while (parent != null && degree > 0) {
            parent = parent.getParent();
            degree--;
        }
        if (degree != 0) {
            throw new IllegalArgumentException();
        }
        return parent;
    }

}
TOP

Related Classes of org.apache.jackrabbit.jcr2spi.state.WorkspaceItemStateFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.