All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.jackrabbit.commons.flat.ItemSequence Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 * 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.commons.flat;

import org.apache.jackrabbit.commons.flat.TreeTraverser.ErrorHandler;
import org.apache.jackrabbit.commons.flat.TreeTraverser.InclusionPolicy;

import javax.jcr.ItemExistsException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;

import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;

/**
 * 

* This class serves as main entry point for obtaining sequences of {@link Node} * s and {@link Property}s. It provides factory methods for creating * {@link NodeSequence}s and {@link PropertySequence}s. *

* *

* NodeSequence and PropertySequence instances provide a flat representation of * a JCR hierarchy rooted at a certain node. They allow iterating over all * items, retrieving items by key, checking whether a given key is mapped, * adding new items and removing existing items. *

* *

* The specifics of the mapping from the flat representation to the JCR * hierarchy are delegated to a {@link TreeManager}. Particularly the * TreeManager specifies the order of the items when retrieved as sequence and * when and how to add and remove intermediate nodes when new items are inserted * or removed. *

* *

* An {@link ErrorHandler} is used to handle exceptions which occur while * traversing the hierarchy. *

* * @see TreeTraverser * @see NodeSequence * @see PropertySequence * @see TreeManager */ public abstract class ItemSequence { /** * The {@link TreeManager} instance managing the mapping between the * sequence view and the JCR hierarchy. */ protected final TreeManager treeManager; /** * The {@link ErrorHandler} instance used for handling exceptions occurring * while traversing the hierarchy. */ protected final ErrorHandler errorHandler; /** * @see TreeManager#getRoot() */ protected final Node root; /** * @see TreeManager#getOrder() */ protected final Comparator order; /** * @see TreeManager#getAutoSave() */ protected final boolean autoSave; /** * Create a new {@link ItemSequence} instance. * * @param treeManager The {@link TreeManager} for managing the mapping * between the sequence view and the JCR hierarchy. * @param errorHandler The {@link ErrorHandler} for handling exceptions * occurring while traversing the hierarchy. * @throws IllegalArgumentException If either treeManager is * null or {@link TreeManager#getRoot()} return * null or {@link TreeManager#getOrder()} return * null. */ protected ItemSequence(TreeManager treeManager, ErrorHandler errorHandler) { super(); if (treeManager == null) { throw new IllegalArgumentException("tree manager must not be null"); } if (treeManager.getRoot() == null) { throw new IllegalArgumentException("root must not be null"); } if (treeManager.getOrder() == null) { throw new IllegalArgumentException("order must not be null"); } this.treeManager = treeManager; this.errorHandler = errorHandler; this.root = treeManager.getRoot(); this.order = treeManager.getOrder(); this.autoSave = treeManager.getAutoSave(); } /** * Create a new {@link NodeSequence} instance. * * @param treeManager The {@link TreeManager} for managing the mapping * between the sequence view and the JCR hierarchy. * @param errorHandler The {@link ErrorHandler} for handling exceptions * occurring while * @return */ public static NodeSequence createNodeSequence(TreeManager treeManager, ErrorHandler errorHandler) { return new NodeSequenceImpl(treeManager, errorHandler); } /** * Create a new {@link NodeSequence} instance. * * @param treeManager The {@link TreeManager} for managing the mapping * between the sequence view and the JCR hierarchy. * @return */ public static NodeSequence createNodeSequence(TreeManager treeManager) { return new NodeSequenceImpl(treeManager, ErrorHandler.IGNORE); } /** * Create a new {@link PropertySequence} instance. * * @param treeManager The {@link TreeManager} for managing the mapping * between the sequence view and the JCR hierarchy. * @param errorHandler The {@link ErrorHandler} for handling exceptions * occurring while * @return */ public static PropertySequence createPropertySequence(TreeManager treeManager, ErrorHandler errorHandler) { return new PropertySequenceImpl(treeManager, errorHandler); } /** * Create a new {@link PropertySequence} instance. * * @param treeManager The {@link TreeManager} for managing the mapping * between the sequence view and the JCR hierarchy. * @return */ public static PropertySequence createPropertySequence(TreeManager treeManager) { return new PropertySequenceImpl(treeManager, ErrorHandler.IGNORE); } /** * Create a new {@link NodeSequence} instance with the same parameterization * as this instance. * * @return */ public NodeSequence getNodeSequence() { return new NodeSequenceImpl(treeManager, errorHandler); } /** * Create a new {@link PropertySequence} instance with the same * parametrization as this instance. * * @return */ public PropertySequence getPropertySequence() { return new PropertySequenceImpl(treeManager, errorHandler); } // -----------------------------------------------------< internal >--- /** * Returns the parent node for the given key. When the key is not present in * this sequence already, the returned node is the node that would contain * that key if it where present. */ protected abstract Node getParent(String key) throws RepositoryException; /** * Returns the predecessor node for the given * key. That is the node * whose key directly precedes the passed key in the order * determined by {@link TreeManager#getOrder()}. There are two cases: *
    *
  • A node with the given key is mapped: then that node is * returned.
  • *
  • A node with the given key is not mapped: the the node * where that would contain that key if present is returned.
  • *
*/ protected final Node getPredecessor(String key) throws RepositoryException { Node p = root; Node n; while ((n = getPredecessor(p, key)) != null) { p = n; } return p; } /** * Returns the direct predecessor of key amongst * node's child nodes wrt. to {@link TreeManager#getOrder()}. * Returns null if either node has no child nodes * or node is a leaf (see {@link TreeManager#isLeaf(Node)}) or * key is smaller than all the keys of all child nodes of * node. */ protected final Node getPredecessor(Node node, String key) throws RepositoryException { if (!node.hasNodes() || treeManager.isLeaf(node)) { return null; } // Shortcut for exact match try { return node.getNode(key); } catch (PathNotFoundException ignore) { } // Search for direct predecessor of key in the nodes children // todo performance: for ordered nodes use binary search NodeIterator childNodes = node.getNodes(); Node p = null; while (childNodes.hasNext()) { Node n = childNodes.nextNode(); String childKey = n.getName(); if (order.compare(key, childKey) > 0 && (p == null || order.compare(childKey, p.getName()) > 0)) { p = n; } } return p; } /** * Returns the successor node for the given * key. That is the node * whose key directly succeeds the passed key in the order * determined by {@link TreeManager#getOrder()}. There are two cases: *
    *
  • A node with the given key is mapped: then that node is * returned.
  • *
  • A node with the given key is not mapped: the the node * where that would contain that key if present is returned.
  • *
*/ protected final Node getSuccessor(Node node, String key) throws RepositoryException { if (!node.hasNodes() || treeManager.isLeaf(node)) { return null; } // Shortcut for exact match try { return node.getNode(key); } catch (PathNotFoundException ignore) { } // Search for direct successor of key in the nodes children // todo performance: for ordered nodes use binary search NodeIterator childNodes = node.getNodes(); Node s = null; while (childNodes.hasNext()) { Node n = childNodes.nextNode(); String childKey = n.getName(); if (order.compare(key, childKey) < 0 && (s == null || order.compare(childKey, s.getName()) < 0)) { s = n; } } return s; } /** * Returns the node with the minimal key wrt. {@link TreeManager#getOrder()}. * For the empty sequence this is {@link TreeManager#getRoot()}. */ protected final Node getMinimal() throws RepositoryException { Node p = null; Node n = root; while ((n = getMinimal(n)) != null) { p = n; } return p; } /** * Returns the node amongst the child nodes of node whose key * is minimal wrt. {@link TreeManager#getOrder()}. Returns null * id either node has no child nodes or node is a * leaf (see {@link TreeManager#isLeaf(Node)}). */ protected final Node getMinimal(Node node) throws RepositoryException { if (!node.hasNodes() || treeManager.isLeaf(node)) { return null; } // Search for minimal key in the nodes children // todo performance: for ordered nodes use binary search NodeIterator childNodes = node.getNodes(); Node p = childNodes.nextNode(); String minKey = p.getName(); while (childNodes.hasNext()) { Node n = childNodes.nextNode(); if (order.compare(n.getName(), minKey) < 0) { p = n; minKey = p.getName(); } } return p; } /** * Rename the path of the node with the minimal key. That is, assuming * node is the node with the minimal key (see * {@link #getMinimal()}), this method renames every segment of the path of * node up to {@link TreeManager#getRoot()} to key * . Note: If node is not the node with the minimal * key, the behavior of this method is not specified. */ protected final void renamePath(Node node, String key) throws RepositoryException { if (!treeManager.isRoot(node)) { Node p = node.getParent(); renamePath(p, key); Session s = node.getSession(); s.move(node.getPath(), p.getPath() + "/" + key); // If orderable, move to first child node if (p.getPrimaryNodeType().hasOrderableChildNodes()) { p.orderBefore(key, p.getNodes().nextNode().getName()); } } } protected static class NodeSequenceImpl extends ItemSequence implements NodeSequence { private final InclusionPolicy inclusionPolicy = new InclusionPolicy() { public boolean include(Node node) { try { return treeManager.isLeaf(node); } catch (RepositoryException e) { return false; } } }; public NodeSequenceImpl(TreeManager treeManager, ErrorHandler errorHandler) { super(treeManager, errorHandler); } public Iterator iterator() { return TreeTraverser.nodeIterator(root, errorHandler, inclusionPolicy); } public Node getItem(String key) throws RepositoryException { return getParent(key).getNode(key); } public boolean hasItem(String key) throws RepositoryException { return getParent(key).hasNode(key); } public Node addNode(String key, String primaryNodeTypeName) throws RepositoryException { Node parent = getOrCreateParent(key); if (parent.hasNode(key)) { throw new ItemExistsException(key); } Node n; if (parent.getPrimaryNodeType().hasOrderableChildNodes()) { Node dest = getSuccessor(parent, key); n = parent.addNode(key, primaryNodeTypeName); parent.orderBefore(key, dest == null ? null : dest.getName()); } else { n = parent.addNode(key, primaryNodeTypeName); } treeManager.split(this, parent, n); if (autoSave) { parent.getSession().save(); } return n; } public void removeNode(String key) throws RepositoryException { Node parent = getParent(key); Node n = parent.getNode(key); n.remove(); treeManager.join(this, parent, n); if (autoSave) { parent.getSession().save(); } } @Override public Node getParent(String key) throws RepositoryException { Node p = getPredecessor(key); if (treeManager.isLeaf(p) && !treeManager.isRoot(p)) { return p.getParent(); } else { return p; } } private Node getOrCreateParent(String key) throws RepositoryException { Node p = getParent(key); if (treeManager.isRoot(p)) { Node min = getMinimal(); if (min != null) { p = min.getParent(); renamePath(p, key); } } return p; } } protected static class PropertySequenceImpl extends ItemSequence implements PropertySequence { private final InclusionPolicy inclusionPolicy = new InclusionPolicy() { private final Set ignoredProperties = treeManager.getIgnoredProperties(); public boolean include(Property property) { try { return !ignoredProperties.contains(property.getName()); } catch (RepositoryException e) { return false; } } }; public PropertySequenceImpl(TreeManager treeManager, ErrorHandler errorHandler) { super(treeManager, errorHandler); } public Iterator iterator() { return TreeTraverser.propertyIterator(getNodeSequence().iterator(), errorHandler, inclusionPolicy); } public Property getItem(String key) throws RepositoryException { return getParent(key).getProperty(key); } public boolean hasItem(String key) throws RepositoryException { return getParent(key).hasProperty(key); } public Property addProperty(String key, Value value) throws RepositoryException { Node parent = getOrCreateParent(key); if (parent.hasProperty(key)) { throw new ItemExistsException(key); } Property p = parent.setProperty(key, value); treeManager.split(this, parent, p); if (autoSave) { p.getSession().save(); } return p; } public void removeProperty(String key) throws RepositoryException { Node parent = getParent(key); Property p = parent.getProperty(key); p.remove(); treeManager.join(this, parent, p); if (autoSave) { parent.getSession().save(); } } @Override public Node getParent(String key) throws RepositoryException { return getPredecessor(key); } private Node getOrCreateParent(String key) throws RepositoryException { Node p = getParent(key); if (treeManager.isRoot(p)) { Node min = getMinimal(); if (min != null) { p = min; renamePath(p, key); } } return p; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy