View Javadoc

1   /*
2    * Copyright (c) 2012 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.eclipse.jetty.spdy.api;
18  
19  import java.util.Arrays;
20  import java.util.Collections;
21  import java.util.Iterator;
22  import java.util.LinkedHashMap;
23  import java.util.LinkedHashSet;
24  import java.util.Map;
25  import java.util.Set;
26  
27  /**
28   * <p>A container for name/value pairs, known as headers.</p>
29   * <p>A {@link Header} is composed of a case-insensitive name string and
30   * of a case-sensitive set of value strings.</p>
31   * <p>The implementation of this class is not thread safe.</p>
32   */
33  public class Headers implements Iterable<Headers.Header>
34  {
35      private final Map<String, Header> headers;
36  
37      /**
38       * <p>Creates an empty modifiable {@link Headers} instance.</p>
39       * @see #Headers(Headers, boolean)
40       */
41      public Headers()
42      {
43          headers = new LinkedHashMap<>();
44      }
45  
46      /**
47       * <p>Creates a {@link Headers} instance by copying the headers from the given
48       * {@link Headers} and making it (im)mutable depending on the given {@code immutable} parameter</p>
49       *
50       * @param original the {@link Headers} to copy headers from
51       * @param immutable whether this instance is immutable
52       */
53      public Headers(Headers original, boolean immutable)
54      {
55          Map<String, Header> copy = new LinkedHashMap<>();
56          copy.putAll(original.headers);
57          headers = immutable ? Collections.unmodifiableMap(copy) : copy;
58      }
59  
60      @Override
61      public boolean equals(Object obj)
62      {
63          if (this == obj)
64              return true;
65          if (obj == null || getClass() != obj.getClass())
66              return false;
67          Headers that = (Headers)obj;
68          return headers.equals(that.headers);
69      }
70  
71      @Override
72      public int hashCode()
73      {
74          return headers.hashCode();
75      }
76  
77      /**
78       * @return a set of header names
79       */
80      public Set<String> names()
81      {
82          Set<String> result = new LinkedHashSet<>();
83          for (Header header : headers.values())
84              result.add(header.name);
85          return result;
86      }
87  
88      /**
89       * @param name the header name
90       * @return the {@link Header} with the given name, or null if no such header exists
91       */
92      public Header get(String name)
93      {
94          return headers.get(name.trim().toLowerCase());
95      }
96  
97      /**
98       * <p>Inserts or replaces the given name/value pair as a single-valued {@link Header}.</p>
99       *
100      * @param name the header name
101      * @param value the header value
102      */
103     public void put(String name, String value)
104     {
105         name = name.trim();
106         Header header = new Header(name, value.trim());
107         headers.put(name.toLowerCase(), header);
108     }
109 
110     /**
111      * <p>Inserts or replaces the given {@link Header}, mapped to the {@link Header#name() header's name}</p>
112      *
113      * @param header the header to add
114      */
115     public void put(Header header)
116     {
117         if (header != null)
118             headers.put(header.name().toLowerCase(), header);
119     }
120 
121     /**
122      * <p>Adds the given value to a header with the given name, creating a {@link Header} is none exists
123      * for the given name.</p>
124      *
125      * @param name the header name
126      * @param value the header value to add
127      */
128     public void add(String name, String value)
129     {
130         name = name.trim();
131         Header header = headers.get(name.toLowerCase());
132         if (header == null)
133         {
134             header = new Header(name, value.trim());
135             headers.put(name.toLowerCase(), header);
136         }
137         else
138         {
139             header = new Header(header.name(), header.value() + "," + value.trim());
140             headers.put(name.toLowerCase(), header);
141         }
142     }
143 
144     /**
145      * <p>Removes the {@link Header} with the given name</p>
146      *
147      * @param name the name of the header to remove
148      * @return the removed header, or null if no such header existed
149      */
150     public Header remove(String name)
151     {
152         name = name.trim();
153         return headers.remove(name.toLowerCase());
154     }
155 
156     /**
157      * <p>Empties this {@link Headers} instance from all headers</p>
158      * @see #isEmpty()
159      */
160     public void clear()
161     {
162         headers.clear();
163     }
164 
165     /**
166      * @return whether this {@link Headers} instance is empty
167      */
168     public boolean isEmpty()
169     {
170         return headers.isEmpty();
171     }
172 
173     /**
174      * @return the number of headers
175      */
176     public int size()
177     {
178         return headers.size();
179     }
180 
181     /**
182      * @return an iterator over the {@link Header} present in this instance
183      */
184     @Override
185     public Iterator<Header> iterator()
186     {
187         return headers.values().iterator();
188     }
189 
190     @Override
191     public String toString()
192     {
193         return headers.toString();
194     }
195 
196     /**
197      * <p>A named list of string values.</p>
198      * <p>The name is case-sensitive and there must be at least one value.</p>
199      */
200     public static class Header
201     {
202         private final String name;
203         private final String[] values;
204 
205         private Header(String name, String value, String... values)
206         {
207             this.name = name;
208             this.values = new String[values.length + 1];
209             this.values[0] = value;
210             if (values.length > 0)
211                 System.arraycopy(values, 0, this.values, 1, values.length);
212         }
213 
214         @Override
215         public boolean equals(Object obj)
216         {
217             if (this == obj)
218                 return true;
219             if (obj == null || getClass() != obj.getClass())
220                 return false;
221             Header that = (Header)obj;
222             return name.equals(that.name) && Arrays.equals(values, that.values);
223         }
224 
225         @Override
226         public int hashCode()
227         {
228             int result = name.hashCode();
229             result = 31 * result + Arrays.hashCode(values);
230             return result;
231         }
232 
233         /**
234          * @return the header's name
235          */
236         public String name()
237         {
238             return name;
239         }
240 
241         /**
242          * @return the first header's value
243          */
244         public String value()
245         {
246             return values[0];
247         }
248 
249         /**
250          * <p>Attempts to convert the result of {@link #value()} to an integer,
251          * returning it if the conversion is successful; returns null if the
252          * result of {@link #value()} is null.</p>
253          *
254          * @return the result of {@link #value()} converted to an integer, or null
255          * @throws NumberFormatException if the conversion fails
256          */
257         public Integer valueAsInt()
258         {
259             final String value = value();
260             return value == null ? null : Integer.valueOf(value);
261         }
262 
263         /**
264          * @return the header's values
265          */
266         public String[] values()
267         {
268             return values;
269         }
270 
271         /**
272          * @return whether the header has multiple values
273          */
274         public boolean hasMultipleValues()
275         {
276             return values.length > 1;
277         }
278 
279         @Override
280         public String toString()
281         {
282             return Arrays.toString(values);
283         }
284     }
285 }