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.juneau.internal; 018 019import static java.util.Collections.*; 020import static java.util.stream.Collectors.*; 021 022import java.util.*; 023 024import org.apache.juneau.utils.*; 025 026/** 027 * A linked hashmap with reverse key lookup by value. 028 * 029 * @param <K> The key type. 030 * @param <V> The value type. 031 */ 032public class BiMap<K,V> implements Map<K,V> { 033 034 //----------------------------------------------------------------------------------------------------------------- 035 // Static 036 //----------------------------------------------------------------------------------------------------------------- 037 038 /** 039 * Create a new builder for this class. 040 * 041 * @param <K> The key type. 042 * @param <V> The value type. 043 * @return A new builder. 044 */ 045 public static <K,V> Builder<K,V> create() { 046 return new Builder<>(); 047 } 048 049 //----------------------------------------------------------------------------------------------------------------- 050 // Builder 051 //----------------------------------------------------------------------------------------------------------------- 052 053 /** 054 * Builder class. 055 * 056 * @param <K> The key type. 057 * @param <V> The value type. 058 */ 059 public static class Builder<K,V> { 060 final HashMap<K,V> map = new LinkedHashMap<>(); 061 boolean unmodifiable; 062 063 /** 064 * Adds a value to this map. 065 * 066 * @param key The key. 067 * @param value The value. 068 * @return This object. 069 */ 070 public Builder<K,V> add(K key, V value) { 071 map.put(key, value); 072 return this; 073 } 074 075 /** 076 * Makes this map unmodifiable. 077 * 078 * @return This object. 079 */ 080 public Builder<K,V> unmodifiable() { 081 unmodifiable = true; 082 return this; 083 } 084 085 /** 086 * Build the differences. 087 * 088 * @return A new {@link BeanDiff} object. 089 */ 090 public BiMap<K,V> build() { 091 return new BiMap<>(this); 092 } 093 094 } 095 096 //----------------------------------------------------------------------------------------------------------------- 097 // Instance 098 //----------------------------------------------------------------------------------------------------------------- 099 100 private final Map<K,V> forward; 101 private final Map<V,K> reverse; 102 103 /** 104 * Constructor. 105 * 106 * @param builder The builder for this object. 107 */ 108 public BiMap(Builder<K,V> builder) { 109 Map<K,V> forward = builder.map.entrySet().stream().filter(x -> x.getKey() != null && x.getValue() != null).collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); 110 Map<V,K> reverse = builder.map.entrySet().stream().filter(x -> x.getKey() != null && x.getValue() != null).collect(toMap(Map.Entry::getValue, Map.Entry::getKey)); 111 this.forward = builder.unmodifiable ? unmodifiableMap(forward) : forward; 112 this.reverse = builder.unmodifiable ? unmodifiableMap(reverse) : reverse; 113 } 114 115 /** 116 * Gets the key that is currently mapped to the specified value. 117 * 118 * @param value The value to return. 119 * @return The key matching the value. 120 */ 121 public K getKey(V value) { 122 return reverse.get(value); 123 } 124 125 126 @Override /* Map */ 127 public int size() { 128 return forward.size(); 129 } 130 131 @Override /* Map */ 132 public boolean isEmpty() { 133 return forward.isEmpty(); 134 } 135 136 @Override /* Map */ 137 public boolean containsKey(Object key) { 138 return forward.containsKey(key); 139 } 140 141 @Override /* Map */ 142 public boolean containsValue(Object value) { 143 return reverse.containsKey(value); 144 } 145 146 @Override /* Map */ 147 public V get(Object key) { 148 return forward.get(key); 149 } 150 151 @Override /* Map */ 152 public V put(K key, V value) { 153 reverse.put(value, key); 154 return forward.put(key, value); 155 } 156 157 @Override /* Map */ 158 public V remove(Object key) { 159 V value = forward.remove(key); 160 reverse.remove(value); 161 return value; 162 } 163 164 @Override /* Map */ 165 public void putAll(Map<? extends K,? extends V> m) { 166 forward.putAll(m); 167 m.entrySet().forEach(x -> reverse.put(x.getValue(), x.getKey())); 168 } 169 170 @Override /* Map */ 171 public void clear() { 172 forward.clear(); 173 reverse.clear(); 174 } 175 176 @Override /* Map */ 177 public Set<K> keySet() { 178 return forward.keySet(); 179 } 180 181 @Override /* Map */ 182 public Collection<V> values() { 183 return forward.values(); 184 } 185 186 @Override /* Map */ 187 public Set<Entry<K,V>> entrySet() { 188 return forward.entrySet(); 189 } 190}