All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.s9api.streams.XdmCollectors Maven / Gradle / Ivy

There is a newer version: 12.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2023 Saxonica Limited
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


package net.sf.saxon.s9api.streams;

import net.sf.saxon.s9api.*;
import net.sf.saxon.transpile.CSharp;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Stream;

/**
 * This class contains a number of static methods that deliver implementations of the {@link java.util.stream.Collector}
 * interface suitable for use with streams processing XDM nodes and other items.
 * 

For example, the method {@code asNode} can be used in an expression such as * {@code XdmNode n = x.select(child("author")).collect(asNode())} to indicate that the content of the stream * delivered by the {@code select()} call is to be delivered as a single XdmNode object, and that an exception * should occur if the result is anything other than a single node.

*

Although these methods can be used directly as arguments to {@link Stream#collect}, it is usually more convenient * to use them indirectly, the form of terminal operations on the class {@link XdmStream}, which extends {@link Stream}. * So a more usual usage would be {@code XdmNode n = x.select(child("author")).asNode()}

*/ public class XdmCollectors { /** * Unchecked exception that occurs when a collector method such as {@link #asAtomic} or {@link #asOptionalNode} * is called, and the sequence contains more than one item. */ public static class MultipleItemException extends RuntimeException {} /** * Abstract superclass for collectors of XDM items * @param the type of the result of the collector, for example {@code XdmNode} or Optional<XdmNode> * @param the type of the items in the stream, for example {@code XdmNode} or {@code XdmAtomicValue} */ private abstract static class XdmCollector implements Collector, R> { protected void onEmpty() { } protected void onMultiple() { } protected I convert(XdmItem item) { //noinspection unchecked return (I)item; } protected abstract R makeResult(List list); @Override public Supplier> supplier() { return CSharp.methodRef(ArrayList::new); } @Override public BiConsumer, XdmItem> accumulator() { return (list, next) -> { I item = convert(next); if (!list.isEmpty()) { onMultiple(); } list.add(item); }; } @Override public BinaryOperator> combiner() { return (list1, list2) -> { list1.addAll(list2); if (list1.size() > 1) { onMultiple(); } return list1; }; } @Override public Function, R> finisher() { return list -> { if (list.isEmpty()) { onEmpty(); } return makeResult(list); }; } @Override public Set characteristics() { return Collections.emptySet(); } } /** * This method provides a Collector that returns the content of a stream as an {@link XdmValue} * @return a collector that returns the single node delivered by the stream, or null if the stream is empty * @throws ClassCastException if the stream contains an item that is not a node */ public static XdmCollector asXdmValue() { return new XdmCollector() { @Override protected XdmValue makeResult(List list) { return new XdmValue(list); } }; } /** * This method provides a Collector that returns the content of a stream as a single {@link XdmNode}. * @return a collector that returns the single node delivered by the stream * @throws NoSuchElementException if the stream is empty * @throws MultipleItemException if the stream contains more than one node * @throws ClassCastException if the stream contains an item that is not a node */ public static XdmCollector asNode() { return new XdmCollector() { @Override protected void onEmpty() { throw new NoSuchElementException(); } @Override protected void onMultiple() { throw new MultipleItemException(); } @Override protected XdmNode convert(XdmItem item) { return (XdmNode)item; // Deliberate ClassCastException if not a node } @Override protected XdmNode makeResult(List list) { return list.get(0); } }; } /** * This method provides a Collector that returns the content of a stream as an optional {@link XdmNode} * (that is, as an instance of Optional<XdmNode>) * * @return a collector that returns the single node delivered by the stream, or * {@code Optional#empty()} if the stream is empty * @throws MultipleItemException if the stream contains more than one node * @throws ClassCastException if the stream contains an item that is not a node */ public static XdmCollector, XdmNode> asOptionalNode() { return new XdmCollector, XdmNode>() { @Override protected void onEmpty() { } @Override protected void onMultiple() { throw new MultipleItemException(); } @Override protected XdmNode convert(XdmItem item) { return (XdmNode) item; // Deliberate ClassCastException if not a node } @Override protected Optional makeResult(List list) { return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0)); } }; } /** * This method provides a Collector that returns the content of a stream as a list of {@link XdmNode} objects * (that is, as an instance of List<XdmNode>) * * @return a collector that returns the single node delivered by the stream, or null if the stream is empty * @throws ClassCastException if the stream contains an item that is not a node */ public static XdmCollector, XdmNode> asListOfNodes() { return new XdmCollector, XdmNode>() { @Override protected void onEmpty() { } @Override protected void onMultiple() { } @Override protected XdmNode convert(XdmItem item) { return (XdmNode) item; // Deliberate ClassCastException if not a node } @Override protected List makeResult(List list) { return list; } }; } /** * This method provides a Collector that returns the content of a stream as a list of atomic values * (that is, as an instance of List<XdmAtomicValue>) * * @return a collector that returns the list of atomic values delivered by the stream * @throws ClassCastException if the stream contains an item that is not an atomic value */ public static XdmCollector, XdmAtomicValue> asListOfAtomic() { return new XdmCollector, XdmAtomicValue>() { @Override protected void onEmpty() { } @Override protected void onMultiple() { } @Override protected XdmAtomicValue convert(XdmItem item) { return (XdmAtomicValue) item; // Deliberate ClassCastException if not an atomic value } @Override protected List makeResult(List list) { return list; } }; } /** * This method provides a Collector that returns the content of a stream as an optional atomic value * (that is, as an instance of Optional<XdmAtomicValue>) * * @return a collector that returns the single atomic value delivered by the stream, or * {@link Optional#empty()} if the stream is empty * @throws ClassCastException if the stream contains an item that is not a node */ public static XdmCollector, XdmAtomicValue> asOptionalAtomic() { return new XdmCollector, XdmAtomicValue>() { @Override protected void onEmpty() { } @Override protected void onMultiple() { throw new MultipleItemException(); } @Override protected XdmAtomicValue convert(XdmItem item) { return (XdmAtomicValue) item; // Deliberate ClassCastException if not an atomic value } @Override protected Optional makeResult(List list) { return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0)); } }; } /** * This method provides a Collector that returns the content of a stream as a single atomic * value, that is, an instance of {@link XdmAtomicValue}. * The stream must deliver a single atomic value. * * @return a collector that returns the string value of the single item delivered by the stream * @throws NoSuchElementException if the stream is empty * @throws MultipleItemException if the stream contains more than one item * @throws ClassCastException if the stream delivers an item that is not an atomic value */ public static XdmCollector asAtomic() { return new XdmCollector() { @Override protected void onEmpty() { throw new NoSuchElementException(); } @Override protected void onMultiple() { throw new MultipleItemException(); } @Override protected XdmAtomicValue convert(XdmItem item) { return (XdmAtomicValue) item; // Deliberate ClassCastException if not an atomic value } @Override protected XdmAtomicValue makeResult(List list) { return list.get(0); } }; } /** * This method provides a Collector that returns the content of a stream as an optional String * (that is, as an instance of Optional<String>) * The stream must deliver either nothing, or a single {@code XdmItem}; the collector returns * the string value of that item. * * @return a collector that returns the string value of the single item delivered by the stream, if any * @throws MultipleItemException if the stream contains more than one item * @throws UnsupportedOperationException if the stream contains an item with no string value (such * as a function item or an element with element-only content) */ public static XdmCollector, XdmItem> asOptionalString() { return new XdmCollector, XdmItem>() { @Override protected void onMultiple() { throw new MultipleItemException(); } @Override protected Optional makeResult(List list) { return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0).getStringValue()); } }; } /** * This method provides a Collector that returns the content of a stream as an optional String * (that is, as an instance of Optional<String>) * The stream must deliver a single {@code XdmItem}; the collector returns the string value * of that item. * * @return a collector that returns the string value of the single item delivered by the stream, if any * @throws MultipleItemException if the stream contains more than one item * @throws NoSuchElementException if the stream is empty * @throws UnsupportedOperationException if the stream contains an item with no string value (such * as a function item or an element with element-only content) */ public static XdmCollector asString() { return new XdmCollector() { @Override protected void onEmpty() { throw new NoSuchElementException(); } @Override protected void onMultiple() { throw new MultipleItemException(); } @Override protected String makeResult(List list) { return list.get(0).getStringValue(); } }; } }