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

org.elasticsearch.painless.Augmentation Maven / Gradle / Ivy

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch 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.elasticsearch.painless;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.ObjIntConsumer;
import java.util.function.Predicate;
import java.util.function.ToDoubleFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/** Additional methods added to classes. These must be static methods with receiver as first argument */
public class Augmentation {
    
    // static methods only!
    private Augmentation() {}

    /** Exposes List.size() as getLength(), so that .length shortcut works on lists */
    public static  int getLength(List receiver) {
        return receiver.size();
    }

    /** Exposes Matcher.group(String) as namedGroup(String), so it doesn't conflict with group(int) */
    public static String namedGroup(Matcher receiver, String name) {
        return receiver.group(name);
    }
    
    // some groovy methods on iterable
    // see http://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/Iterable.html
    
    /** Iterates over the contents of an iterable, and checks whether a predicate is valid for at least one element. */
    public static  boolean any(Iterable receiver, Predicate predicate) {
        for (T t : receiver) {
            if (predicate.test(t)) {
                return true;
            }
        }
        return false;
    }
    
    /** Converts this Iterable to a Collection. Returns the original Iterable if it is already a Collection. */
    public static  Collection asCollection(Iterable receiver) {
        if (receiver instanceof Collection) {
            return (Collection)receiver;
        }
        List list = new ArrayList<>();
        for (T t : receiver) {
            list.add(t);
        }
        return list;
    }
    
    /** Converts this Iterable to a List. Returns the original Iterable if it is already a List. */
    public static  List asList(Iterable receiver) {
        if (receiver instanceof List) {
            return (List)receiver;
        }
        List list = new ArrayList<>();
        for (T t : receiver) {
            list.add(t);
        }
        return list;
    }
    
    /** Counts the number of occurrences which satisfy the given predicate from inside this Iterable. */ 
    public static  int count(Iterable receiver, Predicate predicate) {
        int count = 0;
        for (T t : receiver) {
            if (predicate.test(t)) {
                count++;
            }
        }
        return count;
    }
    
    // instead of covariant overrides for every possibility, we just return receiver as 'def' for now
    // that way if someone chains the calls, everything works.

    /** Iterates through an Iterable, passing each item to the given consumer. */
    public static  Object each(Iterable receiver, Consumer consumer) {
        receiver.forEach(consumer);
        return receiver;
    }
    
    /** 
     * Iterates through an iterable type, passing each item and the item's index 
     * (a counter starting at zero) to the given consumer.
     */
    public static  Object eachWithIndex(Iterable receiver, ObjIntConsumer consumer) {
        int count = 0;
        for (T t : receiver) {
            consumer.accept(t, count++);
        }
        return receiver;
    }
    
    /**
     * Used to determine if the given predicate is valid (i.e. returns true for all items in this iterable).
     */
    public static  boolean every(Iterable receiver, Predicate predicate) {
        for (T t : receiver) {
            if (predicate.test(t) == false) {
                return false;
            }
        }
        return true;
    }
    
    /**
     * Iterates through the Iterable transforming items using the supplied function and 
     * collecting any non-null results. 
     */
    public static  List findResults(Iterable receiver, Function filter) {
        List list = new ArrayList<>();
        for (T t: receiver) {
           U result = filter.apply(t);
           if (result != null) {
               list.add(result);
           }
        }
        return list;
    }
    
    /**
     * Sorts all Iterable members into groups determined by the supplied mapping function. 
     */
    public static  Map> groupBy(Iterable receiver, Function mapper) {
        Map> map = new LinkedHashMap<>();
        for (T t : receiver) {
            U mapped = mapper.apply(t);
            List results = map.get(mapped);
            if (results == null) {
                results = new ArrayList<>();
                map.put(mapped, results);
            }
            results.add(t);
        }
        return map;
    }
    
    /**
     * Concatenates the toString() representation of each item in this Iterable, 
     * with the given String as a separator between each item. 
     */
    public static  String join(Iterable receiver, String separator) {
        StringBuilder sb = new StringBuilder();
        for (T t : receiver) {
            if (sb.length() > 0) {
                sb.append(separator);
            }
            sb.append(t);
        }
        return sb.toString();
    }
    
    /**
     * Sums the result of an Iterable
     */
    public static  double sum(Iterable receiver) {
        double sum = 0;
        for (T t : receiver) {
            sum += t.doubleValue();
        }
        return sum;
    }
    
    /**
     * Sums the result of applying a function to each item of an Iterable. 
     */
    public static  double sum(Iterable receiver, ToDoubleFunction function) {
        double sum = 0;
        for (T t : receiver) {
            sum += function.applyAsDouble(t);
        }
        return sum;
    }
    
    // some groovy methods on collection
    // see http://docs.groovy-lang.org/latest/html/groovy-jdk/java/util/Collection.html
    
    /**
     * Iterates through this collection transforming each entry into a new value using 
     * the function, returning a list of transformed values. 
     */
    public static  List collect(Collection receiver, Function function) {
        List list = new ArrayList<>();
        for (T t : receiver) {
            list.add(function.apply(t));
        }
        return list;
    }
    
    /**
     * Iterates through this collection transforming each entry into a new value using 
     * the function, adding the values to the specified collection.
     */
    public static  Object collect(Collection receiver, Collection collection, Function function) {
        for (T t : receiver) {
            collection.add(function.apply(t));
        }
        return collection;
    }
    
    /**
     * Finds the first value matching the predicate, or returns null.
     */
    public static  T find(Collection receiver, Predicate predicate) {
        for (T t : receiver) {
            if (predicate.test(t)) {
                return t;
            }
        }
        return null;
    }
    
    /**
     * Finds all values matching the predicate, returns as a list
     */
    public static  List findAll(Collection receiver, Predicate predicate) {
        List list = new ArrayList<>();
        for (T t : receiver) {
            if (predicate.test(t)) {
                list.add(t);
            }
        }
        return list;
    }
    
    /**
     * Iterates through the collection calling the given function for each item 
     * but stopping once the first non-null result is found and returning that result. 
     * If all results are null, null is returned. 
     */
    public static  Object findResult(Collection receiver, Function function) {
        return findResult(receiver, null, function);
    }
    
    /**
     * Iterates through the collection calling the given function for each item 
     * but stopping once the first non-null result is found and returning that result. 
     * If all results are null, defaultResult is returned.
     */
    public static  Object findResult(Collection receiver, Object defaultResult, Function function) {
        for (T t : receiver) {
            U value = function.apply(t);
            if (value != null) {
                return value;
            }
        }
        return defaultResult;
    }
    
    /**
     * Splits all items into two collections based on the predicate. 
     * The first list contains all items which match the closure expression. The second list all those that don't. 
     */
    public static  List> split(Collection receiver, Predicate predicate) {
        List matched = new ArrayList<>();
        List unmatched = new ArrayList<>();
        List> result = new ArrayList<>(2);
        result.add(matched);
        result.add(unmatched);
        for (T t : receiver) {
            if (predicate.test(t)) {
                matched.add(t);
            } else {
                unmatched.add(t);
            }
        }
        return result;
    }
    
    // some groovy methods on map
    // see http://docs.groovy-lang.org/latest/html/groovy-jdk/java/util/Map.html
    
    /**
     * Iterates through this map transforming each entry into a new value using 
     * the function, returning a list of transformed values. 
     */
    public static  List collect(Map receiver, BiFunction function) {
        List list = new ArrayList<>();
        for (Map.Entry kvPair : receiver.entrySet()) {
            list.add(function.apply(kvPair.getKey(), kvPair.getValue()));
        }
        return list;
    }
    
    /**
     * Iterates through this map transforming each entry into a new value using 
     * the function, adding the values to the specified collection.
     */
    public static  Object collect(Map receiver, Collection collection, BiFunction function) {
        for (Map.Entry kvPair : receiver.entrySet()) {
            collection.add(function.apply(kvPair.getKey(), kvPair.getValue()));
        }
        return collection;
    }
    
    /** Counts the number of occurrences which satisfy the given predicate from inside this Map */ 
    public static  int count(Map receiver, BiPredicate predicate) {
        int count = 0;
        for (Map.Entry kvPair : receiver.entrySet()) {
            if (predicate.test(kvPair.getKey(), kvPair.getValue())) {
                count++;
            }
        }
        return count;
    }
    
    /** Iterates through a Map, passing each item to the given consumer. */
    public static  Object each(Map receiver, BiConsumer consumer) {
        receiver.forEach(consumer);
        return receiver;
    }
    
    /**
     * Used to determine if the given predicate is valid (i.e. returns true for all items in this map).
     */
    public static  boolean every(Map receiver, BiPredicate predicate) {
        for (Map.Entry kvPair : receiver.entrySet()) {
            if (predicate.test(kvPair.getKey(), kvPair.getValue()) == false) {
                return false;
            }
        }
        return true;
    }
    
    /**
     * Finds the first entry matching the predicate, or returns null.
     */
    public static  Map.Entry find(Map receiver, BiPredicate predicate) {
        for (Map.Entry kvPair : receiver.entrySet()) {
            if (predicate.test(kvPair.getKey(), kvPair.getValue())) {
                return kvPair;
            }
        }
        return null;
    }
    
    /**
     * Finds all values matching the predicate, returns as a map.
     */
    public static  Map findAll(Map receiver, BiPredicate predicate) {
        // try to preserve some properties of the receiver (see the groovy javadocs)
        final Map map;
        if (receiver instanceof TreeMap) {
            map = new TreeMap<>();
        } else {
            map = new LinkedHashMap<>();
        }
        for (Map.Entry kvPair : receiver.entrySet()) {
            if (predicate.test(kvPair.getKey(), kvPair.getValue())) {
                map.put(kvPair.getKey(), kvPair.getValue());
            }
        }
        return map;
    }
    
    /**
     * Iterates through the map calling the given function for each item 
     * but stopping once the first non-null result is found and returning that result. 
     * If all results are null, null is returned. 
     */
    public static  Object findResult(Map receiver, BiFunction function) {
        return findResult(receiver, null, function);
    }
    
    /**
     * Iterates through the map calling the given function for each item 
     * but stopping once the first non-null result is found and returning that result. 
     * If all results are null, defaultResult is returned.
     */
    public static  Object findResult(Map receiver, Object defaultResult, BiFunction function) {
        for (Map.Entry kvPair : receiver.entrySet()) {
            T value = function.apply(kvPair.getKey(), kvPair.getValue());
            if (value != null) {
                return value;
            }
        }
        return defaultResult;
    }
    
    /**
     * Iterates through the map transforming items using the supplied function and 
     * collecting any non-null results. 
     */
    public static  List findResults(Map receiver, BiFunction filter) {
        List list = new ArrayList<>();
        for (Map.Entry kvPair : receiver.entrySet()) {
           T result = filter.apply(kvPair.getKey(), kvPair.getValue());
           if (result != null) {
               list.add(result);
           }
        }
        return list;
    }
    
    /**
     * Sorts all Map members into groups determined by the supplied mapping function. 
     */
    public static  Map> groupBy(Map receiver, BiFunction mapper) {
        Map> map = new LinkedHashMap<>();
        for (Map.Entry kvPair : receiver.entrySet()) {
            T mapped = mapper.apply(kvPair.getKey(), kvPair.getValue());
            Map results = map.get(mapped);
            if (results == null) {
                // try to preserve some properties of the receiver (see the groovy javadocs)
                if (receiver instanceof TreeMap) {
                    results = new TreeMap<>();
                } else {
                    results = new LinkedHashMap<>();
                }
                map.put(mapped, results);
            }
            results.put(kvPair.getKey(), kvPair.getValue());
        }
        return map;
    }

    // CharSequence augmentation
    /**
     * Replace all matches. Similar to {@link Matcher#replaceAll(String)} but allows you to customize the replacement based on the match.
     */
    public static String replaceAll(CharSequence receiver, Pattern pattern, Function replacementBuilder) {
        Matcher m = pattern.matcher(receiver);
        if (false == m.find()) {
            // CharSequqence's toString is *supposed* to always return the characters in the sequence as a String
            return receiver.toString();
        }
        StringBuffer result = new StringBuffer(initialBufferForReplaceWith(receiver));
        do {
            m.appendReplacement(result, Matcher.quoteReplacement(replacementBuilder.apply(m)));
        } while (m.find());
        m.appendTail(result);
        return result.toString();
    }

    /**
     * Replace the first match. Similar to {@link Matcher#replaceFirst(String)} but allows you to customize the replacement based on the
     * match.
     */
    public static String replaceFirst(CharSequence receiver, Pattern pattern, Function replacementBuilder) {
        Matcher m = pattern.matcher(receiver);
        if (false == m.find()) {
            // CharSequqence's toString is *supposed* to always return the characters in the sequence as a String
            return receiver.toString();
        }
        StringBuffer result = new StringBuffer(initialBufferForReplaceWith(receiver));
        m.appendReplacement(result, Matcher.quoteReplacement(replacementBuilder.apply(m)));
        m.appendTail(result);
        return result.toString();
    }

    /**
     * The initial size of the {@link StringBuilder} used for {@link #replaceFirst(CharSequence, Pattern, Function)} and
     * {@link #replaceAll(CharSequence, Pattern, Function)} for a particular sequence. We ape
     * {{@link StringBuilder#StringBuilder(CharSequence)} here and add 16 extra chars to the buffer to have a little room for growth.
     */
    private static int initialBufferForReplaceWith(CharSequence seq) {
        return seq.length() + 16;
    }
}