View Javadoc
1   /**
2    * This file Copyright (c) 2003-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.templating.functions;
35  
36  import info.magnolia.jcr.predicate.NodeTypePredicate;
37  import info.magnolia.jcr.util.NodeUtil;
38  
39  import java.util.List;
40  
41  import javax.jcr.Node;
42  import javax.jcr.RepositoryException;
43  
44  /**
45   * A utility class to navigate amongst the siblings of a given node.
46   * This is not synchronized.
47   */
48  public class SiblingsHelper {
49      /**
50       * Instantiates a SiblingsHelper representing the siblings of the given node and of the same type.
51       */
52      public static SiblingsHelper of(Node node) throws RepositoryException {
53          return new SiblingsHelper(node, filterForTypeOf(node));
54      }
55  
56      private static NodeTypePredicate filterForTypeOf(Node node) throws RepositoryException {
57          return new NodeTypePredicate(node.getPrimaryNodeType().getName());
58      }
59  
60      private final List<Node> siblings;
61      private final int lastIndex;
62      private Node current = null;
63      private int currentIndex = -1;
64  
65      private SiblingsHelper(Node node, NodeTypePredicate filter) throws RepositoryException {
66          this.siblings = NodeUtil.asList(NodeUtil.getNodes(node.getParent(), filter));
67          this.lastIndex = siblings.size() - 1;
68          this.current = node;
69          // can't use indexOf to determine current index, as getParent().getChildren() returns a different instance of the node
70          for (int i = 0; i <= lastIndex; i++) {
71              final Node c = siblings.get(i);
72              if (c.getIdentifier().equals(current.getIdentifier())) {
73                  this.currentIndex = i;
74                  break;
75              }
76          }
77          if (currentIndex < 0) {
78              throw new IllegalStateException("Given node not found in its own siblings.");
79          }
80      }
81  
82      // next() and prev() are not called getNext() and getPrevious() because they change the state.
83      public Node next() {
84          // TODO : check if iterator hasNext();
85          this.currentIndex = currentIndex + 1;
86          this.current = siblings.get(currentIndex);
87          return current;
88      }
89  
90      public Node prev() {
91          // TODO : check if hasPrevious();
92          this.currentIndex = currentIndex - 1;
93          this.current = siblings.get(currentIndex);
94          return current;
95      }
96  
97      public Node goTofirst() {
98          this.currentIndex = 0;
99          this.current = siblings.get(currentIndex);
100         return current;
101     }
102 
103     public Node goTolast() {
104         this.currentIndex = siblings.size() - 1;
105         this.current = siblings.get(currentIndex);
106         return current;
107     }
108 
109     public Node getCurrent() {
110         return current;
111     }
112 
113     /**
114      * Returns the zero-based index of the current node.
115      */
116     public int getIndex() {
117         return currentIndex;
118     }
119 
120     public boolean isFirst() {
121         return currentIndex == 0;
122     }
123 
124     public boolean isLast() {
125         return currentIndex == lastIndex;
126     }
127 
128 }