001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.internal; 014 015import static org.apache.juneau.common.internal.StringUtils.*; 016import static org.apache.juneau.common.internal.ThrowableUtils.*; 017import static org.apache.juneau.internal.ClassUtils.*; 018import static org.apache.juneau.internal.ConverterUtils.*; 019 020import java.lang.reflect.*; 021import java.util.*; 022 023import org.apache.juneau.*; 024import org.apache.juneau.collections.*; 025import org.apache.juneau.parser.*; 026 027/** 028 * Builder for maps. 029 * 030 * <h5 class='section'>See Also:</h5><ul> 031 * </ul> 032 * 033 * @param <K> Key type. 034 * @param <V> Value type. 035 */ 036public final class MapBuilder<K,V> { 037 038 private Map<K,V> map; 039 private boolean unmodifiable = false, sparse = false; 040 private Comparator<K> comparator = null; 041 042 private Class<K> keyType; 043 private Class<V> valueType; 044 private Type[] valueTypeArgs; 045 046 /** 047 * Constructor. 048 * 049 * @param keyType The key type. 050 * @param valueType The value type. 051 * @param valueTypeArgs The value type generic arguments if there are any. 052 */ 053 public MapBuilder(Class<K> keyType, Class<V> valueType, Type...valueTypeArgs) { 054 this.keyType = keyType; 055 this.valueType = valueType; 056 this.valueTypeArgs = valueTypeArgs; 057 } 058 059 /** 060 * Constructor. 061 * 062 * @param addTo The map to add to. 063 */ 064 public MapBuilder(Map<K,V> addTo) { 065 this.map = addTo; 066 } 067 068 /** 069 * Builds the map. 070 * 071 * @return A map conforming to the settings on this builder. 072 */ 073 public Map<K,V> build() { 074 if (sparse) { 075 if (map != null && map.isEmpty()) 076 map = null; 077 } else { 078 if (map == null) 079 map = new LinkedHashMap<>(); 080 } 081 if (map != null) { 082 if (comparator != null) { 083 Map<K,V> m2 = new TreeMap<>(comparator); 084 m2.putAll(map); 085 map = m2; 086 } 087 if (unmodifiable) 088 map = Collections.unmodifiableMap(map); 089 } 090 return map; 091 } 092 093 /** 094 * When specified, the {@link #build()} method will return <jk>null</jk> if the map is empty. 095 * 096 * <p> 097 * Otherwise {@link #build()} will never return <jk>null</jk>. 098 * 099 * @return This object. 100 */ 101 public MapBuilder<K,V> sparse() { 102 this.sparse = true; 103 return this; 104 } 105 106 /** 107 * When specified, {@link #build()} will return an unmodifiable map. 108 * 109 * @return This object. 110 */ 111 public MapBuilder<K,V> unmodifiable() { 112 this.unmodifiable = true; 113 return this; 114 } 115 116 /** 117 * Forces the existing set to be copied instead of appended to. 118 * 119 * @return This object. 120 */ 121 public MapBuilder<K,V> copy() { 122 if (map != null) 123 map = new LinkedHashMap<>(map); 124 return this; 125 } 126 127 /** 128 * Converts the set into a {@link SortedMap}. 129 * 130 * @return This object. 131 */ 132 @SuppressWarnings("unchecked") 133 public MapBuilder<K,V> sorted() { 134 return sorted((Comparator<K>)Comparator.naturalOrder()); 135 } 136 137 /** 138 * Converts the set into a {@link SortedMap} using the specified comparator. 139 * 140 * @param comparator The comparator to use for sorting. 141 * @return This object. 142 */ 143 public MapBuilder<K,V> sorted(Comparator<K> comparator) { 144 this.comparator = comparator; 145 return this; 146 } 147 148 /** 149 * Appends the contents of the specified map into this map. 150 * 151 * <p> 152 * This is a no-op if the value is <jk>null</jk>. 153 * 154 * @param value The map to add to this map. 155 * @return This object. 156 */ 157 public MapBuilder<K,V> addAll(Map<K,V> value) { 158 if (value != null) { 159 if (map == null) 160 map = new LinkedHashMap<>(value); 161 else 162 map.putAll(value); 163 } 164 return this; 165 } 166 167 /** 168 * Adds a single entry to this map. 169 * 170 * @param key The map key. 171 * @param value The map value. 172 * @return This object. 173 */ 174 public MapBuilder<K,V> add(K key, V value) { 175 if (map == null) 176 map = new LinkedHashMap<>(); 177 map.put(key, value); 178 return this; 179 } 180 181 /** 182 * Adds entries to this list via JSON object strings. 183 * 184 * @param values The JSON object strings to parse and add to this list. 185 * @return This object. 186 */ 187 public MapBuilder<K,V> addJson(String...values) { 188 return addAny((Object[])values); 189 } 190 191 /** 192 * Adds arbitrary values to this list. 193 * 194 * <p> 195 * Objects can be any of the following: 196 * <ul> 197 * <li>Maps of key/value types convertible to the key/value types of this map. 198 * <li>JSON object strings parsed and convertible to the key/value types of this map. 199 * </ul> 200 * 201 * @param values The values to add. 202 * @return This object. 203 */ 204 @SuppressWarnings("unchecked") 205 public MapBuilder<K,V> addAny(Object...values) { 206 if (keyType == null || valueType == null) 207 throw new IllegalStateException("Unknown key and value types. Cannot use this method."); 208 try { 209 for (Object o : values) { 210 if (o != null) { 211 if (o instanceof Map) { 212 ((Map<Object,Object>)o).forEach((k,v) -> add(toType(k, keyType), toType(v, valueType, valueTypeArgs))); 213 } else if (isJsonObject(o, false)) { 214 JsonMap.ofJson(o.toString()).forEach((k,v) -> add(toType(k, keyType), toType(v, valueType, valueTypeArgs))); 215 } else { 216 throw new BasicRuntimeException("Invalid object type {0} passed to addAny()", className(o)); 217 } 218 } 219 } 220 } catch (ParseException e) { 221 throw asRuntimeException(e); 222 } 223 return this; 224 } 225 226 /** 227 * Adds a list of key/value pairs to this map. 228 * 229 * @param pairs The pairs to add. 230 * @return This object. 231 */ 232 @SuppressWarnings("unchecked") 233 public MapBuilder<K,V> addPairs(Object...pairs) { 234 if (pairs.length % 2 != 0) 235 throw new IllegalArgumentException("Odd number of parameters passed into AMap.ofPairs()"); 236 for (int i = 0; i < pairs.length; i+=2) 237 add((K)pairs[i], (V)pairs[i+1]); 238 return this; 239 } 240}