001/*
002 *  Copyright 2011 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.workspaces.repository.jcr;
017
018import javax.jcr.Node;
019import javax.jcr.NodeIterator;
020import javax.jcr.RepositoryException;
021
022/**
023 * Node group helper.
024 */
025public final class NodeGroupHelper
026{
027    
028    /** The maximum children to display per node. */
029    public static final int MAX_CHILDREN_PER_NODE = 100;
030    
031    private NodeGroupHelper()
032    {
033        // Hides the default constructor.
034    }
035    
036    /**
037     * Get the node's full path, including virtual path segments.
038     * @param node the node of which to get the path.
039     * @return the computed path.
040     * @throws RepositoryException if a repository error occurs.
041     */
042    public static String getPathWithGroups(Node node) throws RepositoryException
043    {
044        StringBuffer fullPath = new StringBuffer();
045        
046        // Compute and append all ancestor paths.
047        int depth = node.getDepth();
048        for (int i = 0; i < depth; i++)
049        {
050            Node ancestor = (Node) node.getAncestor(i);
051            Node ancestorChild = (Node) node.getAncestor(i + 1);
052            
053            if (!ancestor.getName().equals(""))
054            {
055                fullPath.append('/').append(ancestor.getName());
056                
057                int index = ancestor.getIndex();
058                if (index > 1)
059                {
060                    fullPath.append('[').append(index).append(']');
061                }
062            }
063            
064            // Append the virtual paths if needed.
065            fullPath.append(getGroupPaths(ancestorChild, MAX_CHILDREN_PER_NODE));
066        }
067        
068        // Append the node path itself.
069        fullPath.append('/').append(node.getName());
070        
071        int index = node.getIndex();
072        if (index > 1)
073        {
074            fullPath.append('[').append(index).append(']');
075        }
076        
077        return fullPath.toString();
078    }
079    
080    /**
081     * Get the virtual a node's path relative to its parent.
082     * If the parent node has more than a specified node count, "virtual" paths
083     * are added between the parent and the node.
084     * The empty string is returned if the parent doesn't have more nodes than the maximum.
085     * @param node the node to get the virtual path of.
086     * @param maxNodesPerGroup the maximum node per group.
087     * @return the virtual path part of the node in its parent.
088     * @throws RepositoryException if a repository error occurs.
089     */
090    private static String getGroupPaths(Node node, int maxNodesPerGroup) throws RepositoryException
091    {
092        StringBuilder nodePath = new StringBuilder();
093        
094        NodeIterator children = node.getParent().getNodes();
095        long childCount = children.getSize();
096        if (childCount > maxNodesPerGroup)
097        {
098            // Compute virtual path.
099            int level = (int) Math.ceil(Math.log(childCount) / Math.log(maxNodesPerGroup)) - 1;
100            
101            int position = 0;
102            boolean found = false;
103            while (children.hasNext() && !found)
104            {
105                if (children.nextNode().isSame(node))
106                {
107                    found = true;
108                }
109                else
110                {
111                    position++;
112                }
113            }
114            
115            // The position has been found.
116            for (int j = level; j > 0; j--)
117            {
118                int currentGroupCount = (int) Math.pow(maxNodesPerGroup, j);
119                int groupIndex = position / currentGroupCount;
120                
121                int startPos = groupIndex * currentGroupCount + 1;
122                int endPos = Math.min(startPos + currentGroupCount - 1, (int) childCount);
123                
124                nodePath.append('/').append(startPos).append("...").append(endPos);
125            }
126        }
127        
128        return nodePath.toString();
129    }
130    
131}