001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.chain.web;
018
019import java.util.Enumeration;
020import java.util.Map;
021import java.util.Set;
022import java.util.function.BiConsumer;
023import java.util.function.Consumer;
024import java.util.function.Function;
025import java.util.function.Supplier;
026
027/**
028 * Implementation of {@code Map} for mutable parameters with a
029 * parameter-provider.
030 *
031 * @param <P> the type of the parameter-provider
032 * @param <T> the type of results supplied by this parameters
033 *
034 * @author Graff Stefan
035 * @since Chain 1.3
036 */
037public class MutableParameterMap<P, T> extends ParameterMap<P, T> {
038
039    /**
040     * Consumer which removes the given public or private parameter.
041     * All values associated with the name are removed.
042     */
043    private final Consumer<String> removeConsumer;
044
045    /**
046     * BiConsumer which stores an {@link Object} in this parameter.
047     */
048    private final BiConsumer<String, T> setConsumer;
049
050    /**
051     * The constructor for an mutable parameter-map.
052     *
053     * @param parameter the parameter-provider
054     * @param valueFunction Function to return the value of a parameter
055     * @param namesSupplier Supplier to return all names of the parameter
056     * @param removeConsumer Consumer to remove a value of the parameter
057     * @param setConsumer BiConsumer to stores a value of the parameter
058     */
059    public MutableParameterMap(final P parameter, final Function<String, T> valueFunction,
060            final Supplier<Enumeration<String>> namesSupplier, final Consumer<String> removeConsumer,
061            final BiConsumer<String, T> setConsumer) {
062
063        super(parameter, valueFunction, namesSupplier);
064        this.removeConsumer = removeConsumer;
065        this.setConsumer = setConsumer;
066    }
067
068    /**
069     * Removes all of the mappings from this parameter-map.
070     * The parameter-map will be empty after this call returns.
071     */
072    @Override
073    public void clear() {
074        for (String key : keySet()) {
075            removeConsumer.accept(key);
076        }
077    }
078
079    /**
080     * Returns {@code true} if this parameter-map maps one or more keys
081     * to the specified value.
082     *
083     * @param value value whose presence in this parameter-map is to be
084     *        tested
085     *
086     * @return {@code true} if this parameter-map maps one or more keys
087     *         to the specified value
088     */
089    @Override
090    public boolean containsValue(Object value) {
091        return value != null && super.containsValue(value);
092    }
093
094    /**
095     * Returns a {@link Set} view of the mappings contained in this
096     * parameter-map. The set is not backed by the parameter-map, so
097     * changes to the parameter-map are not reflected in the set,
098     * and vice-versa.
099     *
100     * @return a set view of the mappings contained in this
101     *         parameter-map
102     */
103    @Override
104    public Set<Map.Entry<String, T>> entrySet() {
105        return entrySet(true);
106    }
107
108    /**
109     * Associates the specified value with the specified key in this
110     * parameter-map. If the parameter-map previously contained a
111     * mapping for the key, the old value is replaced.
112     *
113     * @param key key with which the specified value is to be associated
114     * @param value value to be associated with the specified key
115     *
116     * @return the previous value associated with {@code key}, or
117     *         {@code null} if there was no mapping for {@code key}.
118     *         (A {@code null} return can also indicate that the
119     *         parameter-map previously associated {@code null} with
120     *         {@code key}.)
121     */
122    @Override
123    public T put(String key, T value) {
124        if (value == null) {
125            return remove(key);
126        }
127        final T previous = getValueFunction().apply(key);
128        setConsumer.accept(key, value);
129        return previous;
130    }
131
132    /**
133     * Copies all of the mappings from the specified map to this
134     * parameter-map. These mappings will replace any mappings that
135     * this parameter-map had for any of the keys currently in the
136     * specified map.
137     *
138     * @param map mappings to be stored in this parameter-map
139     *
140     * @throws NullPointerException if the specified map is null
141     */
142    @Override
143    public void putAll(Map<? extends String, ? extends T> map) {
144        map.forEach(this::put);
145    }
146
147    /**
148     * Removes the mapping for the specified key from this
149     * parameter-map if present.
150     *
151     * @param key key whose mapping is to be removed from the
152     *        parameter-map
153     *
154     * @return the previous value associated with {@code key}, or
155     *         {@code null} if there was no mapping for {@code key}.
156     *         (A {@code null} return can also indicate that the
157     *         parameter-map previously associated {@code null} with
158     *         {@code key}.)
159     */
160    @Override
161    public T remove(Object key) {
162        final String skey = key(key);
163        final T previous = getValueFunction().apply(skey);
164        removeConsumer.accept(skey);
165        return previous;
166    }
167
168    /**
169     * Returns the hash code value for this parameter-map. The
170     * hash code of a parameter-map is defined to be the sum of
171     * the hash codes of each entry in the parameter-map's
172     * {@code entrySet()} view. This ensures that {@code m1.equals(m2)}
173     * implies that {@code m1.hashCode()==m2.hashCode()} for any two
174     * parameter-maps {@code m1} and {@code m2}, as required by the
175     * general contract of {@link Object#hashCode}.
176     *
177     * @implSpec
178     * This implementation calls the {@code hashCode()} from the
179     * parameter-provider.
180     *
181     * @return the hash code value for this parameter-map
182     */
183    @Override
184    public int hashCode() {
185        return super.hashCode();
186    }
187
188    /**
189     * Compares the specified object with this parameter-map for equality.
190     * Returns {@code true} if the given object is also a parameter-map
191     * and the two parameter-maps represent the same mappings. More formally,
192     * two parameter-maps {@code m1} and {@code m2} represent the same
193     * mappings if {@code m1.entrySet().equals(m2.entrySet())}.
194     *
195     * @implSpec
196     * This implementation first checks if the specified object is this
197     * parameter-map; if so it returns {@code true}. Then, it checks if
198     * the specified object is the identical class this parameter-map; if
199     * not, it returns {@code false}. If so, it calls the {@code equals()}
200     * from the parameter-provider and returns its return-code.
201     *
202     * @param obj object to be compared for equality with this
203     *        parameter-map
204     *
205     * @return {@code true} if the specified object is equal to this
206     *         parameter-map
207     */
208    @Override
209    public boolean equals(Object obj) {
210        return super.equals(obj);
211    }
212}