init
This commit is contained in:
76
java/org/apache/el/stream/Optional.java
Normal file
76
java/org/apache/el/stream/Optional.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 javax.el.ELException;
|
||||
import javax.el.LambdaExpression;
|
||||
|
||||
import org.apache.el.util.MessageFactory;
|
||||
|
||||
public class Optional {
|
||||
|
||||
private final Object obj;
|
||||
|
||||
static final Optional EMPTY = new Optional(null);
|
||||
|
||||
Optional(Object obj) {
|
||||
this.obj = obj;
|
||||
}
|
||||
|
||||
|
||||
public Object get() throws ELException {
|
||||
if (obj == null) {
|
||||
throw new ELException(MessageFactory.get("stream.optional.empty"));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void ifPresent(LambdaExpression le) {
|
||||
if (obj != null) {
|
||||
le.invoke(obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Object orElse(Object other) {
|
||||
if (obj == null) {
|
||||
return other;
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Object orElseGet(Object le) {
|
||||
if (obj == null) {
|
||||
// EL 3.0 specification says parameter is LambdaExpression but it
|
||||
// may already have been evaluated. If that is the case, the
|
||||
// original parameter will have been checked to ensure it was a
|
||||
// LambdaExpression before it was evaluated.
|
||||
|
||||
if (le instanceof LambdaExpression) {
|
||||
return ((LambdaExpression) le).invoke((Object[]) null);
|
||||
} else {
|
||||
return le;
|
||||
}
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
522
java/org/apache/el/stream/Stream.java
Normal file
522
java/org/apache/el/stream/Stream.java
Normal file
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
113
java/org/apache/el/stream/StreamELResolverImpl.java
Normal file
113
java/org/apache/el/stream/StreamELResolverImpl.java
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.beans.FeatureDescriptor;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import javax.el.ELContext;
|
||||
import javax.el.ELResolver;
|
||||
|
||||
public class StreamELResolverImpl extends ELResolver {
|
||||
|
||||
@Override
|
||||
public Object getValue(ELContext context, Object base, Object property) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType(ELContext context, Object base, Object property) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(ELContext context, Object base, Object property,
|
||||
Object value) {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly(ELContext context, Object base, Object property) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context,
|
||||
Object base) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getCommonPropertyType(ELContext context, Object base) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(ELContext context, Object base, Object method,
|
||||
Class<?>[] paramTypes, Object[] params) {
|
||||
|
||||
if ("stream".equals(method) && params.length == 0) {
|
||||
if (base.getClass().isArray()) {
|
||||
context.setPropertyResolved(true);
|
||||
return new Stream(new ArrayIterator(base));
|
||||
} else if (base instanceof Collection) {
|
||||
context.setPropertyResolved(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<Object> collection = (Collection<Object>) base;
|
||||
return new Stream(collection.iterator());
|
||||
}
|
||||
}
|
||||
|
||||
// Not for handling by this resolver
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static class ArrayIterator implements Iterator<Object> {
|
||||
|
||||
private final Object base;
|
||||
private final int size;
|
||||
private int index = 0;
|
||||
|
||||
public ArrayIterator(Object base) {
|
||||
this.base = base;
|
||||
size = Array.getLength(base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return size > index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object next() {
|
||||
try {
|
||||
return Array.get(base, index++);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user