001/*
002 * Shredzone Commons
003 *
004 * Copyright (C) 2014 Richard "Shred" Körber
005 *   http://commons.shredzone.org
006 *
007 * This program is free software: you can redistribute it and/or modify
008 * it under the terms of the GNU Library General Public License as
009 * published by the Free Software Foundation, either version 3 of the
010 * License, or (at your option) any later version.
011 *
012 * This program is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU Library General Public License
018 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
019 */
020package org.shredzone.commons.xml;
021
022import java.util.Spliterator;
023import java.util.function.Consumer;
024import java.util.stream.Stream;
025import java.util.stream.StreamSupport;
026
027import javax.annotation.Nonnull;
028import javax.annotation.ParametersAreNonnullByDefault;
029
030import org.w3c.dom.Node;
031import org.w3c.dom.NodeList;
032
033/**
034 * A {@link Spliterator} for {@link NodeList}. Used internally by {@link XQuery}.
035 *
036 * @author Richard "Shred" Körber
037 */
038@ParametersAreNonnullByDefault
039public class NodeListSpliterator implements Spliterator<Node> {
040
041    private final NodeList list;
042    private int pos;
043    private int end;
044
045    /**
046     * Creates a new {@link NodeListSpliterator}.
047     *
048     * @param list
049     *            {@link NodeList} to create a {@link NodeListSpliterator} of
050     */
051    public NodeListSpliterator(NodeList list) {
052        this(list, 0, list.getLength());
053    }
054
055    /**
056     * Creates a new {@link NodeListSpliterator} with limited range. Used internally for
057     * splitting.
058     *
059     * @param list
060     *            {@link NodeList}
061     * @param pos
062     *            Beginning of range, inclusive
063     * @param end
064     *            Ending of range, exlusive
065     */
066    private NodeListSpliterator(NodeList list, int pos, int end) {
067        this.list = list;
068        this.pos = pos;
069        this.end = end;
070    }
071
072    /**
073     * Creates a new {@link Stream} of {@link Node} for this spliterator.
074     * <p>
075     * This is a convenience call. It just invokes
076     * {@link StreamSupport#stream(Spliterator, boolean)}.
077     *
078     * @return {@link Stream} of nodes
079     */
080    public @Nonnull Stream<Node> stream() {
081        return StreamSupport.stream(this, false);
082    }
083
084    @Override
085    public boolean tryAdvance(Consumer<? super Node> action) {
086        if (pos < end) {
087            action.accept(list.item(pos));
088            pos++;
089            return true;
090        }
091
092        return false;
093    }
094
095    @Override
096    public Spliterator<Node> trySplit() {
097        int remain = end - pos;
098        if (remain > 1) {
099            int half = remain >> 1;
100            int split = pos + half;
101            Spliterator<Node> result = new NodeListSpliterator(list, split, end);
102            end = split;
103            return result;
104        }
105        return null;
106    }
107
108    @Override
109    public long estimateSize() {
110        return ((long) end) - ((long) pos);
111    }
112
113    @Override
114    public int characteristics() {
115        return ORDERED | DISTINCT | SIZED | NONNULL | SUBSIZED;
116    }
117
118}