/** * Copyright 2013 the original author or authors. * * Licensed 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 io.neba.core.util; import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.nodetype.NodeType; import java.util.Iterator; import java.util.LinkedList; import java.util.NoSuchElementException; import java.util.Queue; import static java.util.Arrays.asList; /** * Iterates the {@link NodeType node type} hierarchy of a {@link Node}. * Starts with the {@link Node#getPrimaryNodeType() primary node type} followed by the * {@link Node#getMixinNodeTypes() mixin types directly applied to the node}, * followed by the {@link NodeType#getDeclaredSupertypes() super types of the primary node type}, * recursively.<br /> * The order of a nodes super types is not specified by the JCR API. It is thus considered an implementation * detail of the JCR repository and ignored in this context. * * @author Olaf Otto */ public class NodeTypeHierarchyIterator implements Iterator<String>, Iterable<String> { /** * @param node must not be <code>null</code>. * @return never <code>null</code>. */ public static NodeTypeHierarchyIterator typeHierarchyOf(Node node) { return new NodeTypeHierarchyIterator(node); } private final Queue<NodeType> queue = new LinkedList<>(); private NodeType current; private NodeType next = null; /** * @param node must not be <code>null</code>. */ public NodeTypeHierarchyIterator(final Node node) { if (node == null) { throw new IllegalArgumentException("Constructor argument node must not be null."); } try { this.current = node.getPrimaryNodeType(); this.next = current; // Add the mixin node types applied to the current content node only. final NodeType[] mixinNodeTypes = node.getMixinNodeTypes(); if (mixinNodeTypes != null && mixinNodeTypes.length != 0) { this.queue.addAll(asList(mixinNodeTypes)); } } catch (RepositoryException e) { throw new RuntimeException("Unable to prepare the given node for node type hierarchy traversal.", e); } } public Iterator<String> iterator() { return this; } public boolean hasNext() { return this.next != null || this.current != null && resolveNext(); } private boolean resolveNext() { NodeType[] superTypes = this.current.getDeclaredSupertypes(); if (superTypes != null && superTypes.length != 0) { queue.addAll(asList(superTypes)); } if (!queue.isEmpty()) { this.next = this.queue.poll(); } return this.next != null; } public String next() { if (!hasNext()) { throw new NoSuchElementException(); } this.current = this.next; this.next = null; return this.current.getName(); } public void remove() { throw new UnsupportedOperationException("Remove is unsupported - this is a read-only iterator."); } }