/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.el.stream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; import javax.el.ELException; import javax.el.LambdaExpression; import org.apache.el.lang.ELArithmetic; import org.apache.el.lang.ELSupport; import org.apache.el.util.MessageFactory; public class Stream { private final Iterator<Object> iterator; public Stream(Iterator<Object> iterator) { this.iterator = iterator; } public Stream filter(final LambdaExpression le) { Iterator<Object> downStream = new OpIterator() { @Override protected void findNext() { while (iterator.hasNext()) { Object obj = iterator.next(); if (ELSupport.coerceToBoolean(null, le.invoke(obj), true).booleanValue()) { next = obj; foundNext = true; break; } } } }; return new Stream(downStream); } public Stream map(final LambdaExpression le) { Iterator<Object> downStream = new OpIterator() { @Override protected void findNext() { if (iterator.hasNext()) { Object obj = iterator.next(); next = le.invoke(obj); foundNext = true; } } }; return new Stream(downStream); } public Stream flatMap(final LambdaExpression le) { Iterator<Object> downStream = new OpIterator() { private Iterator<?> inner; @Override protected void findNext() { while (iterator.hasNext() || (inner != null && inner.hasNext())) { if (inner == null || !inner.hasNext()) { inner = ((Stream) le.invoke(iterator.next())).iterator; } if (inner.hasNext()) { next = inner.next(); foundNext = true; break; } } } }; return new Stream(downStream); } public Stream distinct() { Iterator<Object> downStream = new OpIterator() { private Set<Object> values = new HashSet<>(); @Override protected void findNext() { while (iterator.hasNext()) { Object obj = iterator.next(); if (values.add(obj)) { next = obj; foundNext = true; break; } } } }; return new Stream(downStream); } public Stream sorted() { Iterator<Object> downStream = new OpIterator() { private Iterator<Object> sorted = null; @Override protected void findNext() { if (sorted == null) { sort(); } if (sorted.hasNext()) { next = sorted.next(); foundNext = true; } } @SuppressWarnings({ "rawtypes", "unchecked" }) private final void sort() { List list = new ArrayList<>(); while (iterator.hasNext()) { list.add(iterator.next()); } Collections.sort(list); sorted = list.iterator(); } }; return new Stream(downStream); } public Stream sorted(final LambdaExpression le) { Iterator<Object> downStream = new OpIterator() { private Iterator<Object> sorted = null; @Override protected void findNext() { if (sorted == null) { sort(le); } if (sorted.hasNext()) { next = sorted.next(); foundNext = true; } } @SuppressWarnings({ "rawtypes", "unchecked" }) private final void sort(LambdaExpression le) { List list = new ArrayList<>(); Comparator<Object> c = new LambdaExpressionComparator(le); while (iterator.hasNext()) { list.add(iterator.next()); } Collections.sort(list, c); sorted = list.iterator(); } }; return new Stream(downStream); } public Object forEach(final LambdaExpression le) { while (iterator.hasNext()) { le.invoke(iterator.next()); } return null; } public Stream peek(final LambdaExpression le) { Iterator<Object> downStream = new OpIterator() { @Override protected void findNext() { if (iterator.hasNext()) { Object obj = iterator.next(); le.invoke(obj); next = obj; foundNext = true; } } }; return new Stream(downStream); } public Iterator<?> iterator() { return iterator; } public Stream limit(final Number count) { return substream(Integer.valueOf(0), count); } public Stream substream(final Number start) { return substream(start, Integer.valueOf(Integer.MAX_VALUE)); } public Stream substream(final Number start, final Number end) { Iterator<Object> downStream = new OpIterator() { private final int startPos = start.intValue(); private final int endPos = end.intValue(); private int itemCount = 0; @Override protected void findNext() { while (itemCount < startPos && iterator.hasNext()) { iterator.next(); itemCount++; } if (itemCount < endPos && iterator.hasNext()) { itemCount++; next = iterator.next(); foundNext = true; } } }; return new Stream(downStream); } public List<Object> toList() { List<Object> result = new ArrayList<>(); while (iterator.hasNext()) { result.add(iterator.next()); } return result; } public Object[] toArray() { List<Object> result = new ArrayList<>(); while (iterator.hasNext()) { result.add(iterator.next()); } return result.toArray(new Object[result.size()]); } public Optional reduce(LambdaExpression le) { Object seed = null; if (iterator.hasNext()) { seed = iterator.next(); } if (seed == null) { return Optional.EMPTY; } else { return new Optional(reduce(seed, le)); } } public Object reduce(Object seed, LambdaExpression le) { Object result = seed; while (iterator.hasNext()) { result = le.invoke(result, iterator.next()); } return result; } public Optional max() { return compare(true); } public Optional max(LambdaExpression le) { return compare(true, le); } public Optional min() { return compare(false); } public Optional min(LambdaExpression le) { return compare(false, le); } public Optional average() { long count = 0; Number sum = Long.valueOf(0); while (iterator.hasNext()) { count++; sum = ELArithmetic.add(sum, iterator.next()); } if (count == 0) { return Optional.EMPTY; } else { return new Optional(ELArithmetic.divide(sum, Long.valueOf(count))); } } public Number sum() { Number sum = Long.valueOf(0); while (iterator.hasNext()) { sum = ELArithmetic.add(sum, iterator.next()); } return sum; } public Long count() { long count = 0; while (iterator.hasNext()) { iterator.next(); count ++; } return Long.valueOf(count); } public Optional anyMatch(LambdaExpression le) { if (!iterator.hasNext()) { return Optional.EMPTY; } Boolean match = Boolean.FALSE; while (!match.booleanValue() && iterator.hasNext()) { match = (Boolean) le.invoke(iterator.next()); } return new Optional(match); } public Optional allMatch(LambdaExpression le) { if (!iterator.hasNext()) { return Optional.EMPTY; } Boolean match = Boolean.TRUE; while (match.booleanValue() && iterator.hasNext()) { match = (Boolean) le.invoke(iterator.next()); } return new Optional(match); } public Optional noneMatch(LambdaExpression le) { if (!iterator.hasNext()) { return Optional.EMPTY; } Boolean match = Boolean.FALSE; while (!match.booleanValue() && iterator.hasNext()) { match = (Boolean) le.invoke(iterator.next()); } return new Optional(Boolean.valueOf(!match.booleanValue())); } public Optional findFirst() { if (iterator.hasNext()) { return new Optional(iterator.next()); } else { return Optional.EMPTY; } } @SuppressWarnings({ "rawtypes", "unchecked" }) private Optional compare(boolean isMax) { Comparable result = null; if (iterator.hasNext()) { Object obj = iterator.next(); if ((obj instanceof Comparable)) { result = (Comparable) obj; } else { throw new ELException( MessageFactory.get("stream.compare.notComparable")); } } while (iterator.hasNext()) { Object obj = iterator.next(); if ((obj instanceof Comparable)) { if (isMax && ((Comparable) obj).compareTo(result) > 0) { result = (Comparable) obj; } else if (!isMax && ((Comparable) obj).compareTo(result) < 0) { result = (Comparable) obj; } } else { throw new ELException( MessageFactory.get("stream.compare.notComparable")); } } if (result == null) { return Optional.EMPTY; } else { return new Optional(result); } } private Optional compare(boolean isMax, LambdaExpression le) { Object result = null; if (iterator.hasNext()) { Object obj = iterator.next(); result = obj; } while (iterator.hasNext()) { Object obj = iterator.next(); if (isMax && ELSupport.coerceToNumber(null, le.invoke(obj, result), Integer.class).intValue() > 0) { result = obj; } else if (!isMax && ELSupport.coerceToNumber(null, le.invoke(obj, result), Integer.class).intValue() < 0) { result = obj; } } if (result == null) { return Optional.EMPTY; } else { return new Optional(result); } } private static class LambdaExpressionComparator implements Comparator<Object> { private final LambdaExpression le; public LambdaExpressionComparator(LambdaExpression le) { this.le = le; } @Override public int compare(Object o1, Object o2) { return ELSupport.coerceToNumber( null, le.invoke(o1, o2), Integer.class).intValue(); } } private abstract static class OpIterator implements Iterator<Object> { protected boolean foundNext = false; protected Object next; @Override public boolean hasNext() { if (foundNext) { return true; } findNext(); return foundNext; } @Override public Object next() { if (foundNext) { foundNext = false; return next; } findNext(); if (foundNext) { foundNext = false; return next; } else { throw new NoSuchElementException(); } } @Override public void remove() { throw new UnsupportedOperationException(); } protected abstract void findNext(); } }