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 */
016package org.ametys.plugins.repository;
017
018import java.util.List;
019import java.util.NoSuchElementException;
020import java.util.stream.Collectors;
021
022/**
023 * Implementation of a {@link AmetysObjectIterable} based on a list of others {@link AmetysObjectIterable}.
024 * @param <A> the actual type of {@link AmetysObject}s.
025 */
026public class ChainedAmetysObjectIterable<A extends AmetysObject> implements AmetysObjectIterable<A>
027{
028    long _size;
029    private List<AmetysObjectIterable<A>> _iterables;
030    private boolean _sizeComputed;
031    
032    /**
033     * Creates a {@link ChainedAmetysObjectIterable}.
034     * @param iterables a list of {@link AmetysObjectIterable}.
035     */
036    public ChainedAmetysObjectIterable(List<AmetysObjectIterable<A>> iterables)
037    {
038        _iterables = iterables;
039    }
040    
041    public long getSize()
042    {
043        if (!_sizeComputed)
044        {
045            _size = _computeSize();
046        }
047        
048        return _size;
049    }
050    
051    private long _computeSize()
052    {
053        _sizeComputed = true;
054        
055        long size = 0;
056        
057        for (AmetysObjectIterable it : _iterables)
058        {
059            long s = it.getSize();
060            
061            if (s == -1)
062            {
063                return -1;
064            }
065            
066            size += s;
067        }
068        
069        return size;
070    }
071    
072    public AmetysObjectIterator<A> iterator()
073    {
074        List<AmetysObjectIterator<A>> its = _iterables.stream().map(it -> it.iterator()).collect(Collectors.toList());
075        return new ChainedIterator(its);
076    }
077    
078    public void close()
079    {
080        for (AmetysObjectIterable<A> it : _iterables)
081        {
082            it.close();
083        }
084    }
085    
086    class ChainedIterator implements AmetysObjectIterator<A>
087    {
088        private long _position;
089        private int _currentIterator;
090        private List<AmetysObjectIterator<A>> _its;
091        
092        public ChainedIterator(List<AmetysObjectIterator<A>> its)
093        {
094            _its = its;
095        }
096        
097        public long getPosition()
098        {
099            return _position;
100        }
101        
102        public long getSize()
103        {
104            return ChainedAmetysObjectIterable.this.getSize();
105        }
106        
107        public void skip(long skipNum)
108        {
109            if (skipNum == 0)
110            {
111                return;
112            }
113            
114            if (skipNum < 0)
115            {
116                throw new AmetysRepositoryException("Cannot skip with a negative number");
117            }
118            
119            for (long i = skipNum; i > 0; i--)
120            {
121                if (!hasNext())
122                {
123                    throw new NoSuchElementException();
124                }
125                
126                _position++;
127                _its.get(_currentIterator).skip(1);
128            }
129        }
130
131        public boolean hasNext()
132        {
133            while (_currentIterator < _its.size() && !_its.get(_currentIterator).hasNext())
134            {
135                _currentIterator++;
136            }
137            
138            return _currentIterator < _its.size() && _its.get(_currentIterator).hasNext();
139        }
140        
141        public A next()
142        {
143            if (!hasNext())
144            {
145                throw new NoSuchElementException();
146            }
147            
148            _position++;
149            return _its.get(_currentIterator).next();
150        }
151    }
152}