001/* 
002 * Copyright 2018 Leibniz-Institut für Analytische Wissenschaften – ISAS – e.V..
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 de.isas.mztab2.cvmapping;
017
018import java.util.Arrays;
019import java.util.Collection;
020import java.util.Iterator;
021import java.util.List;
022import java.util.Spliterator;
023import java.util.Spliterators;
024import java.util.stream.Collectors;
025import java.util.stream.Stream;
026import java.util.stream.StreamSupport;
027import org.apache.commons.jxpath.JXPathContext;
028import org.apache.commons.jxpath.Pointer;
029import org.apache.commons.lang3.tuple.Pair;
030
031/**
032 * Utility methods for simpler Java 8 compatible handling of JXPath selections
033 * and pointers.
034 *
035 * @author nilshoffmann
036 */
037public final class JxPathElement {
038
039    /**
040     * Wrap the provided pointer as a stream of the given object type.
041     * @param <T> the generic object type
042     * @param pointer the pointer in the object tree
043     * @param type the object's type
044     * @return a typed stream of pairs of pointer and typed object
045     */
046    public static <T> Stream<Pair<Pointer, T>> toStream(
047        Pointer pointer, Class<? extends T> type) {
048        if (pointer.getValue() instanceof Collection) {
049            Collection<Pair<Pointer, T>> coll = JxPathElement.toList(
050                pointer,
051                type);
052            return coll.stream();
053        }
054        return Stream.of(Pair.of(pointer, type.cast(pointer.getValue())));
055    }
056
057    /**
058     * Wrap the provided pointer as a list of the given object type.
059     * @param <T> the generic object type
060     * @param pointer the pointer in the object tree
061     * @param type the object's type
062     * @return a typed list of pairs of pointer and typed object
063     */
064    public static <T> List<Pair<Pointer, T>> toList(
065        Pointer pointer,
066        Class<? extends T> type) {
067        if (pointer.getValue() instanceof Collection) {
068            Collection<?> c = (Collection) pointer.getValue();
069            return c.stream().
070                map((o) ->
071                {
072                    return Pair.of(pointer, (T) type.cast(o));
073                }).
074                collect(Collectors.toList());
075        } else {
076            return Arrays.asList(Pair.of(pointer, (T) type.cast(pointer.
077                getValue())));
078        }
079    }
080
081    /**
082     * 
083     * Returns the elements selected by the xpath expression from the provided JXPathContext as a list of the given object type.
084     * @param <T> the generic object type
085     * @param context the jxpath object tree context
086     * @param xpath the xpath expression used for subtree / element selection
087     * @param type the object's type
088     * @return a typed list of pairs of pointer and typed object
089     */
090    public static <T> List<Pair<Pointer, T>> toList(
091        JXPathContext context, String xpath, Class<? extends T> type) {
092        return toStream(context.iteratePointers(xpath), Pointer.class).
093            map((pointer) ->
094            {
095                return Pair.of(pointer, (T) type.cast(pointer.getValue()));
096            }).
097            collect(Collectors.toList());
098    }
099
100    /**
101     * Creates a typed iterator from an untyped (pre Java 6) iterator.
102     * @param <T> the generic object type
103     * @param iter the original iterator
104     * @param type the object's type
105     * @return a typed iterator of the requested type, backed by the provided iterator.
106     */
107    public static <T> Iterator<T> typedIter(final Iterator iter,
108        Class<? extends T> type) {
109        return new Iterator<T>() {
110            @Override
111            public boolean hasNext() {
112                return iter.hasNext();
113            }
114
115            @Override
116            public T next() {
117                return type.cast(iter.next());
118            }
119        };
120    }
121
122    /**
123     * Creates a typed stream from an untyped (pre Java 6) iterator.
124     * @param <T> the generic object type
125     * @param iter the original iterator
126     * @param type the object's type
127     * @return a typed iterator of the requested type, backed by the provided iterator.
128     * @see typedIter
129     */
130    public static <T> Stream<T> toStream(Iterator iter,
131        Class<? extends T> type) {
132        return toStream(typedIter(iter, type));
133    }
134
135    /**
136     * Wraps the provided typed iterator into a stream, based on Spliterators.
137     * @param <T> the generic object type
138     * @param iter the original iterator
139     * @return a typed (unbounded and ordered) stream, backed by the provided iterator.
140     */
141    public static <T> Stream<T> toStream(Iterator<? extends T> iter) {
142        return StreamSupport.stream(
143            Spliterators.spliteratorUnknownSize(iter, Spliterator.ORDERED),
144            false);
145    }
146}