View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.util;
20  
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Map.Entry;
28  
29  /** 
30   * A multi valued Map.
31   * @param <V> the entry type for multimap values
32   */
33  @SuppressWarnings("serial")
34  public class MultiMap<V> extends HashMap<String,List<V>>
35  {
36      public MultiMap()
37      {
38          super();
39      }
40  
41      public MultiMap(Map<String,List<V>> map)
42      {
43          super(map);
44      }
45  
46      public MultiMap(MultiMap<V> map)
47      {
48          super(map);
49      }
50  
51  
52      /* ------------------------------------------------------------ */
53      /** Get multiple values.
54       * Single valued entries are converted to singleton lists.
55       * @param name The entry key. 
56       * @return Unmodifieable List of values.
57       */
58      public List<V> getValues(String name)
59      {
60          List<V> vals = super.get(name);
61          if((vals == null) || vals.isEmpty()) {
62              return null;
63          }
64          return vals;
65      }
66      
67      /* ------------------------------------------------------------ */
68      /** Get a value from a multiple value.
69       * If the value is not a multivalue, then index 0 retrieves the
70       * value or null.
71       * @param name The entry key.
72       * @param i Index of element to get.
73       * @return Unmodifieable List of values.
74       */
75      public V getValue(String name,int i)
76      {
77          List<V> vals = getValues(name);
78          if(vals == null) {
79              return null;
80          }
81          if (i==0 && vals.isEmpty()) {
82              return null;
83          }
84          return vals.get(i);
85      }
86      
87      
88      /* ------------------------------------------------------------ */
89      /** Get value as String.
90       * Single valued items are converted to a String with the toString()
91       * Object method. Multi valued entries are converted to a comma separated
92       * List.  No quoting of commas within values is performed.
93       * @param name The entry key. 
94       * @return String value.
95       */
96      public String getString(String name)
97      {
98          List<V> vals =get(name);
99          if ((vals == null) || (vals.isEmpty()))
100         {
101             return null;
102         }
103         
104         if (vals.size() == 1)
105         {
106             // simple form.
107             return vals.get(0).toString();
108         }
109         
110         // delimited form
111         StringBuilder values=new StringBuilder(128);
112         for (V e : vals)
113         {
114             if (e != null)
115             {
116                 if (values.length() > 0)
117                     values.append(',');
118                 values.append(e.toString());
119             }
120         }   
121         return values.toString();
122     }
123     
124     /** 
125      * Put multi valued entry.
126      * @param name The entry key. 
127      * @param value The simple value
128      * @return The previous value or null.
129      */
130     public List<V> put(String name, V value) 
131     {
132         if(value == null) {
133             return super.put(name, null);
134         }
135         List<V> vals = new ArrayList<>();
136         vals.add(value);
137         return put(name,vals);
138     }
139 
140     /**
141      * Shorthand version of putAll
142      * @param input the input map
143      */
144     public void putAllValues(Map<String, V> input)
145     {
146         for(Map.Entry<String,V> entry: input.entrySet())
147         {
148             put(entry.getKey(), entry.getValue());
149         }
150     }
151     
152     /* ------------------------------------------------------------ */
153     /** Put multi valued entry.
154      * @param name The entry key. 
155      * @param values The List of multiple values.
156      * @return The previous value or null.
157      */
158     public List<V> putValues(String name, List<V> values) 
159     {
160         return super.put(name,values);
161     }
162     
163     /* ------------------------------------------------------------ */
164     /** Put multi valued entry.
165      * @param name The entry key. 
166      * @param values The array of multiple values.
167      * @return The previous value or null.
168      */
169     @SafeVarargs
170     public final List<V> putValues(String name, V... values) 
171     {
172         List<V> list = new ArrayList<>();
173         list.addAll(Arrays.asList(values));
174         return super.put(name,list);
175     }
176     
177     
178     /* ------------------------------------------------------------ */
179     /** Add value to multi valued entry.
180      * If the entry is single valued, it is converted to the first
181      * value of a multi valued entry.
182      * @param name The entry key. 
183      * @param value The entry value.
184      */
185     public void add(String name, V value) 
186     {
187         List<V> lo = get(name);
188         if(lo == null) {
189             lo = new ArrayList<>();
190         }
191         lo.add(value);
192         super.put(name,lo);
193     }
194 
195     /* ------------------------------------------------------------ */
196     /** Add values to multi valued entry.
197      * If the entry is single valued, it is converted to the first
198      * value of a multi valued entry.
199      * @param name The entry key. 
200      * @param values The List of multiple values.
201      */
202     public void addValues(String name, List<V> values) 
203     {
204         List<V> lo = get(name);
205         if(lo == null) {
206             lo = new ArrayList<>();
207         }
208         lo.addAll(values);
209         put(name,lo);
210     }
211     
212     /* ------------------------------------------------------------ */
213     /** Add values to multi valued entry.
214      * If the entry is single valued, it is converted to the first
215      * value of a multi valued entry.
216      * @param name The entry key. 
217      * @param values The String array of multiple values.
218      */
219     public void addValues(String name, V[] values) 
220     {
221         List<V> lo = get(name);
222         if(lo == null) {
223             lo = new ArrayList<>();
224         }
225         lo.addAll(Arrays.asList(values));
226         put(name,lo);
227     }
228     
229     /**
230      * Merge values.
231      * 
232      * @param map
233      *            the map to overlay on top of this one, merging together values if needed.
234      * @return true if an existing key was merged with potentially new values, false if either no change was made, or there were only new keys.
235      */
236     public boolean addAllValues(MultiMap<V> map)
237     {
238         boolean merged = false;
239 
240         if ((map == null) || (map.isEmpty()))
241         {
242             // done
243             return merged;
244         }
245 
246         for (Map.Entry<String, List<V>> entry : map.entrySet())
247         {
248             String name = entry.getKey();
249             List<V> values = entry.getValue();
250 
251             if (this.containsKey(name))
252             {
253                 merged = true;
254             }
255 
256             this.addValues(name,values);
257         }
258 
259         return merged;
260     }
261     
262     /* ------------------------------------------------------------ */
263     /** Remove value.
264      * @param name The entry key. 
265      * @param value The entry value. 
266      * @return true if it was removed.
267      */
268     public boolean removeValue(String name,V value)
269     {
270         List<V> lo = get(name);
271         if((lo == null)||(lo.isEmpty())) {
272             return false;
273         }
274         boolean ret = lo.remove(value);
275         if(lo.isEmpty()) {
276             remove(name);
277         } else {
278             put(name,lo);
279         }
280         return ret;
281     }
282     
283     /**
284      * Test for a specific single value in the map.
285      * <p>
286      * NOTE: This is a SLOW operation, and is actively discouraged.
287      * @param value the value to search for
288      * @return true if contains simple value
289      */
290     public boolean containsSimpleValue(V value)
291     {
292         for (List<V> vals : values())
293         {
294             if ((vals.size() == 1) && vals.contains(value))
295             {
296                 return true;
297             }
298         }
299         return false;
300     }
301     
302     @Override
303     public String toString()
304     {
305         Iterator<Entry<String, List<V>>> iter = entrySet().iterator();
306         StringBuilder sb = new StringBuilder();
307         sb.append('{');
308         boolean delim = false;
309         while (iter.hasNext())
310         {
311             Entry<String, List<V>> e = iter.next();
312             if (delim)
313             {
314                 sb.append(", ");
315             }
316             String key = e.getKey();
317             List<V> vals = e.getValue();
318             sb.append(key);
319             sb.append('=');
320             if (vals.size() == 1)
321             {
322                 sb.append(vals.get(0));
323             }
324             else
325             {
326                 sb.append(vals);
327             }
328             delim = true;
329         }
330         sb.append('}');
331         return sb.toString();
332     }
333     
334     /* ------------------------------------------------------------ */
335     /** 
336      * @return Map of String arrays
337      */
338     public Map<String,String[]> toStringArrayMap()
339     {
340         HashMap<String,String[]> map = new HashMap<String,String[]>(size()*3/2)
341         {
342             @Override
343             public String toString()
344             {
345                 StringBuilder b=new StringBuilder();
346                 b.append('{');
347                 for (String k:super.keySet())
348                 {
349                     if(b.length()>1)
350                         b.append(',');
351                     b.append(k);
352                     b.append('=');
353                     b.append(Arrays.asList(super.get(k)));
354                 }
355 
356                 b.append('}');
357                 return b.toString();
358             }
359         };
360         
361         for(Map.Entry<String,List<V>> entry: entrySet())
362         {
363             String[] a = null;
364             if (entry.getValue() != null)
365             {
366                 a = new String[entry.getValue().size()];
367                 a = entry.getValue().toArray(a);
368             }
369             map.put(entry.getKey(),a);
370         }
371         return map;
372     }
373 
374 }