/*
 * Licensed 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.jdbi.v3.core.statement;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.jdbi.v3.core.argument.Argument;
import org.jdbi.v3.core.argument.NamedArgumentFinder;
import org.jdbi.v3.core.internal.JdbiOptionals;
/**
 * Represents the arguments bound to a particular statement.
 */
public class Binding {
    private final Map<Integer, Argument> positionals = new HashMap<>();
    private final Map<String, Argument> named = new HashMap<>();
    private final List<NamedArgumentFinder> namedArgumentFinder = new ArrayList<>();

    /**
     * Bind a positional parameter at the given index (0-based).
     * @param position binding position
     * @param argument the argument to bind
     */
    public void addPositional(int position, Argument argument) {
        positionals.put(position, argument);
    }

    /**
     * Bind a named parameter for the given name.
     * @param name bound argument name
     * @param argument the argument to bind
     */
    public void addNamed(String name, Argument argument) {
        this.named.put(name, argument);
    }

    /**
     * Bind a named argument finder.
     * @param args the argument finder to bind
     */
    public void addNamedArgumentFinder(NamedArgumentFinder args) {
        namedArgumentFinder.add(args);
    }

    /**
     * Look up an argument by name.
     *
     * @param name the key to lookup the value of
     * @param ctx the statement context
     *
     * @return the bound Argument
     */
    public Optional<Argument> findForName(String name, StatementContext ctx) {
        if (named.containsKey(name)) {
            return Optional.of(named.get(name));
        }

        return namedArgumentFinder.stream()
                .flatMap(arguments -> JdbiOptionals.stream(arguments.find(name, ctx)))
                .findFirst();
    }

    /**
     * @return the set of known binding names
     */
    public Collection<String> getNames() {
        final Set<String> names = new HashSet<>(named.keySet());
        namedArgumentFinder.forEach(args -> names.addAll(args.getNames()));
        return Collections.unmodifiableSet(names);
    }

    /**
     * Look up an argument by position.
     *
     * @param position starts at 0, not 1
     * @return argument bound to that position
     */
    public Optional<Argument> findForPosition(int position) {
        return Optional.ofNullable(positionals.get(position));
    }

    @Override
    public String toString() {
        String positionalsDescription = positionals.entrySet().stream()
            .map(x -> x.getKey().toString() + ':' + x.getValue())
            .collect(Collectors.joining(","));

        String namedDescription = named.entrySet().stream()
            .map(x -> x.getKey() + ':' + x.getValue())
            .collect(Collectors.joining(","));

        String found = namedArgumentFinder.stream()
            .map(Object::toString)
            .collect(Collectors.joining(","));

        return "{positional:{" + positionalsDescription + "}, named:{" + namedDescription + "}, finder:[" + found + "]}";
    }

    /**
     * Remove all bindings from this Binding.
     */
    public void clear() {
        positionals.clear();
        named.clear();
        namedArgumentFinder.clear();
    }

    /**
     * @return true if there are no bindings yet
     */
    public boolean isEmpty() {
        return positionals.isEmpty() && named.isEmpty() && namedArgumentFinder.isEmpty();
    }
}

@@ -2349,35 +2349,73 @@
     
-if (named.containsKey(name)
+final Argument found = named.get(name);%0A        if (found != null
 ) %7B%0A
@@ -2445,31 +2445,21 @@
 onal.of(
-named.get(name)
+found
 );%0A     

Improve Binding.findForName