001/* 002 * Copyright 2010 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 */ 016 017package org.ametys.plugins.repository; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import javax.jcr.Node; 023import javax.jcr.NodeIterator; 024import javax.jcr.RepositoryException; 025import javax.jcr.Session; 026 027/** 028 * Implementation of {@link AmetysObjectIterable} based on a underlying JCR {@link NodeIterator}.<br><br> 029 * <b>Please note that this implementation only works with NodeIterators containing nodes actually bound to AmetysObjects.<br> 030 * If a node exists in the iterator and does not correspond to an {@link AmetysObject}, an {@link AmetysRepositoryException} will be thrown.</b><br> 031 * Unless {@link AmetysObjectIterator#skip(long)} is called on the underlying Iterator, 032 * results are buffered so that one may safely call {@link #iterator()} more than once, 033 * even if the underlying {@link NodeIterator} will be consumed only once.<br> 034 * If {@link AmetysObjectIterator#skip(long)} is called, any subsequent call to {@link #iterator()} will throw an {@link IllegalStateException}. 035 * @param <A> the actual type of {@link AmetysObject}s. 036 */ 037public class NodeIteratorIterable<A extends AmetysObject> implements AmetysObjectIterable<A> 038{ 039 AmetysObjectResolver _resolver; 040 String _parentPath; 041 Session _session; 042 List<A> _buffer = new ArrayList<>(); 043 boolean _skipCalled; 044 private NodeIterator _iterator; 045 046 047 /** 048 * Creates a {@link NodeIteratorIterable}. 049 * @param resolver the application {@link AmetysObjectResolver}. 050 * @param iterator the underlying {@link NodeIterator}. 051 * @param parentPath the parent path in the Ametys hierarchy of all 052 * {@link AmetysObject} being returned. 053 * @param session the JCR Session corresponding to the specified NodeIterator 054 */ 055 public NodeIteratorIterable(AmetysObjectResolver resolver, NodeIterator iterator, String parentPath, Session session) 056 { 057 _resolver = resolver; 058 _iterator = iterator; 059 _parentPath = parentPath; 060 _session = session; 061 } 062 063 public long getSize() 064 { 065 return _iterator.getSize(); 066 } 067 068 public AmetysObjectIterator<A> iterator() 069 { 070 if (_skipCalled) 071 { 072 throw new IllegalStateException("iterator() cannot be called on NodeIteratorIterable after skip() have been called. Results would be inconsistent."); 073 } 074 075 return new NodeIteratorIterator(_iterator); 076 } 077 078 public void close() 079 { 080 if (_session != null && _session.isLive()) 081 { 082 _session.logout(); 083 } 084 } 085 086 class NodeIteratorIterator implements AmetysObjectIterator<A> 087 { 088 private NodeIterator _it; 089 private int _position; 090 091 public NodeIteratorIterator(NodeIterator it) 092 { 093 _it = it; 094 } 095 096 public long getPosition() 097 { 098 return _position; 099 } 100 101 public long getSize() 102 { 103 return _it.getSize(); 104 } 105 106 public void skip(long skipNum) 107 { 108 if (skipNum > _buffer.size() - _position) 109 { 110 _skipCalled = true; 111 _it.skip(skipNum - (_buffer.size() - _position)); 112 113 for (int i = _buffer.size() - _position; i < skipNum; i++) 114 { 115 _buffer.add(null); 116 } 117 } 118 119 _position += skipNum; 120 } 121 122 public boolean hasNext() 123 { 124 return _position < _buffer.size() || _it.hasNext(); 125 } 126 127 public A next() 128 { 129 if (_position < _buffer.size()) 130 { 131 return _buffer.get(_position++); 132 } 133 134 Node node = _it.nextNode(); 135 _position++; 136 137 A result = null; 138 try 139 { 140 result = _resolver.resolve(_parentPath, node, null, false); 141 return result; 142 } 143 catch (RepositoryException e) 144 { 145 throw new AmetysRepositoryException("An error occured while resolving", e); 146 } 147 finally 148 { 149 // add something to the buffer, even if its null, 150 // so that the buffer size matches the current position 151 _buffer.add(result); 152 } 153 } 154 155 public void remove() 156 { 157 _it.remove(); 158 } 159 } 160}