blob: af173263555bbedc8f59a619b436ac29d0194ec8 [file] [log] [blame]
/*
* 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.sling.testing.mock.sling.builder;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.wrappers.ValueMapDecorator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.osgi.annotation.versioning.ProviderType;
/**
* {@link ValueMap} that does not support changing its content.
* <p>
* All methods that may change the content will throw a
* {@link UnsupportedOperationException}.
* </p>
* <p>
* Static convenience methods provide similar behavior as Guava ImmutableMap
* variants.
* </p>
*/
@ProviderType
public final class ImmutableValueMap implements ValueMap {
private final ValueMap map;
/**
* @param map Value map
*/
ImmutableValueMap(@NotNull ValueMap map) {
this.map = map;
}
/**
* @param map Map
*/
ImmutableValueMap(@NotNull Map<String, Object> map) {
this.map = new ValueMapDecorator(map);
}
@Override
public @Nullable <T> T get(@NotNull String name, @NotNull Class<T> type) {
return this.map.get(name, type);
}
@Override
public @NotNull <T> T get(@NotNull String name, @NotNull T defaultValue) {
return this.map.get(name, defaultValue);
}
@Override
public int size() {
return this.map.size();
}
@Override
public boolean isEmpty() {
return this.map.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return this.map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return this.map.containsValue(value);
}
@Override
public Object get(Object key) {
return this.map.get(key);
}
@Override
public Set<String> keySet() {
return this.map.keySet();
}
@Override
public Collection<Object> values() {
return this.map.values();
}
@Override
public Set<Entry<String, Object>> entrySet() {
return Collections.unmodifiableSet(this.map.entrySet());
}
@Override
public int hashCode() {
return this.map.hashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ImmutableValueMap)) {
return false;
}
return this.map.entrySet().equals(((ImmutableValueMap) obj).map.entrySet());
}
@Override
public String toString() {
return map.toString();
}
// mutable operations not supported
/**
* @deprecated Unsupported operation
*/
@Override
@Deprecated
public Object put(String key, Object value) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation
*/
@Override
@Deprecated
public Object remove(Object key) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation
*/
@Override
@Deprecated
public void putAll(Map<? extends String, ? extends Object> m) {
throw new UnsupportedOperationException();
}
/**
* @deprecated Unsupported operation
*/
@Override
@Deprecated
public void clear() {
throw new UnsupportedOperationException();
}
/**
* Returns the empty map. This map behaves and performs comparably to
* {@link Collections#emptyMap}, and is preferable mainly for consistency
* and maintainability of your code.
* @return ImmutableValueMap
*/
public static @NotNull ImmutableValueMap of() {
return new ImmutableValueMap(ValueMap.EMPTY);
}
/**
* Returns an immutable map containing a single entry. This map behaves and
* performs comparably to {@link Collections#singletonMap} but will not
* accept a null key or value. It is preferable mainly for consistency and
* maintainability of your code.
* @param k1 Key 1
* @param v1 Value 1
* @return ImmutableValueMap
*/
@SuppressWarnings("null")
public static @NotNull ImmutableValueMap of(@NotNull String k1, @NotNull Object v1) {
return new ImmutableValueMap(Collections.singletonMap(k1, v1));
}
/**
* Returns an immutable map containing the given entries, in order.
* @param k1 Key 1
* @param v1 Value 1
* @param k2 Key 2
* @param v2 Value 2
* @return ImmutableValueMap
* @throws IllegalArgumentException if duplicate keys are provided
*/
public static @NotNull ImmutableValueMap of(
@NotNull String k1, @NotNull Object v1, @NotNull String k2, @NotNull Object v2) {
Map<String, Object> map = new HashMap<>();
map.put(k1, v1);
map.put(k2, v2);
return new ImmutableValueMap(map);
}
/**
* Returns an immutable map containing the given entries, in order.
* @param k1 Key 1
* @param v1 Value 1
* @param k2 Key 2
* @param v2 Value 2
* @param k3 Key 3
* @param v3 Value 3
* @return ImmutableValueMap
* @throws IllegalArgumentException if duplicate keys are provided
*/
public static @NotNull ImmutableValueMap of(
@NotNull String k1,
@NotNull Object v1,
@NotNull String k2,
@NotNull Object v2,
@NotNull String k3,
@NotNull Object v3) {
Map<String, Object> map = new HashMap<>();
map.put(k1, v1);
map.put(k2, v2);
map.put(k3, v3);
return new ImmutableValueMap(map);
}
/**
* Returns an immutable map containing the given entries, in order.
* @param k1 Key 1
* @param v1 Value 1
* @param k2 Key 2
* @param v2 Value 2
* @param k3 Key 3
* @param v3 Value 3
* @param k4 Key 4
* @param v4 Value 4
* @return ImmutableValueMap
* @throws IllegalArgumentException if duplicate keys are provided
*/
public static @NotNull ImmutableValueMap of( // NOPMD
@NotNull String k1,
@NotNull Object v1,
@NotNull String k2,
@NotNull Object v2,
@NotNull String k3,
@NotNull Object v3,
@NotNull String k4,
@NotNull Object v4) {
Map<String, Object> map = new HashMap<>();
map.put(k1, v1);
map.put(k2, v2);
map.put(k3, v3);
map.put(k4, v4);
return new ImmutableValueMap(map);
}
/**
* Returns an immutable map containing the given entries, in order.
* @param k1 Key 1
* @param v1 Value 1
* @param k2 Key 2
* @param v2 Value 2
* @param k3 Key 3
* @param v3 Value 3
* @param k4 Key 4
* @param v4 Value 4
* @param k5 Key 5
* @param v5 Value 5
* @return ImmutableValueMap
* @throws IllegalArgumentException if duplicate keys are provided
*/
public static ImmutableValueMap of( // NOPMD
@NotNull String k1,
@NotNull Object v1,
@NotNull String k2,
@NotNull Object v2,
@NotNull String k3,
@NotNull Object v3,
@NotNull String k4,
@NotNull Object v4,
@NotNull String k5,
@NotNull Object v5) {
Map<String, Object> map = new HashMap<>();
map.put(k1, v1);
map.put(k2, v2);
map.put(k3, v3);
map.put(k4, v4);
map.put(k5, v5);
return new ImmutableValueMap(map);
}
// looking for of() with > 5 entries? Use the builder instead.
/**
* Returns a new builder. The generated builder is equivalent to the builder
* created by the {@link Builder} constructor.
* @return Builder
*/
public static @NotNull Builder builder() {
return new Builder();
}
/**
* Returns an immutable map containing the same entries as {@code map}. If
* {@code map} somehow contains entries with duplicate keys (for example, if
* it is a {@code SortedMap} whose comparator is not <i>consistent with
* equals</i>), the results of this method are undefined.
* <p>
* Despite the method name, this method attempts to avoid actually copying
* the data when it is safe to do so. The exact circumstances under which a
* copy will or will not be performed are undocumented and subject to
* change.
* </p>
* @param map Map
* @return ImmutableValueMap
* @throws NullPointerException if any key or value in {@code map} is null
*/
public static @NotNull ImmutableValueMap copyOf(@NotNull Map<String, Object> map) {
if (map instanceof ValueMap) {
return new ImmutableValueMap((ValueMap) map);
} else {
return new ImmutableValueMap(map);
}
}
/**
* Builder interface for {@link ImmutableValueMap}.
*/
public static final class Builder {
private final @NotNull Map<String, Object> map = new HashMap<>();
/**
* Associates {@code key} with {@code value} in the built map. Duplicate
* keys are not allowed, and will cause {@link #build} to fail.
* @param key Key
* @param value value
* @return this
*/
public @NotNull Builder put(@NotNull String key, @NotNull Object value) {
map.put(key, value);
return this;
}
/**
* Adds the given {@code entry} to the map, making it immutable if
* necessary. Duplicate keys are not allowed, and will cause
* {@link #build} to fail.
* @param entry Entry
* @return this
*/
public @NotNull Builder put(@NotNull Entry<String, Object> entry) {
return put(entry.getKey(), entry.getValue());
}
/**
* Associates all of the given map's keys and values in the built map.
* Duplicate keys are not allowed, and will cause {@link #build} to
* fail.
* @param value Value
* @return this
* @throws NullPointerException if any key or value in {@code map} is
* null
*/
public @NotNull Builder putAll(@NotNull Map<String, Object> value) {
map.putAll(value);
return this;
}
/**
* Returns a newly-created immutable map.
* @return ImmutableValueMap
* @throws IllegalArgumentException if duplicate keys were added
*/
public @NotNull ImmutableValueMap build() {
if (map.isEmpty()) {
return ImmutableValueMap.of();
} else {
return new ImmutableValueMap(map);
}
}
}
}