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         headers.put(header.name().toLowerCase(), header);
118     }
119 
120     /**
121      * <p>Adds the given value to a header with the given name, creating a {@link Header} is none exists
122      * for the given name.</p>
123      *
124      * @param name the header name
125      * @param value the header value to add
126      */
127     public void add(String name, String value)
128     {
129         name = name.trim();
130         Header header = headers.get(name.toLowerCase());
131         if (header == null)
132         {
133             header = new Header(name, value.trim());
134             headers.put(name.toLowerCase(), header);
135         }
136         else
137         {
138             header = new Header(header.name(), header.value() + "," + value.trim());
139             headers.put(name.toLowerCase(), header);
140         }
141     }
142 
143     /**
144      * <p>Removes the {@link Header} with the given name</p>
145      *
146      * @param name the name of the header to remove
147      * @return the removed header, or null if no such header existed
148      */
149     public Header remove(String name)
150     {
151         name = name.trim();
152         return headers.remove(name.toLowerCase());
153     }
154 
155     /**
156      * <p>Empties this {@link Headers} instance from all headers</p>
157      * @see #isEmpty()
158      */
159     public void clear()
160     {
161         headers.clear();
162     }
163 
164     /**
165      * @return whether this {@link Headers} instance is empty
166      */
167     public boolean isEmpty()
168     {
169         return headers.isEmpty();
170     }
171 
172     /**
173      * @return the number of headers
174      */
175     public int size()
176     {
177         return headers.size();
178     }
179 
180     /**
181      * @return an iterator over the {@link Header} present in this instance
182      */
183     @Override
184     public Iterator<Header> iterator()
185     {
186         return headers.values().iterator();
187     }
188 
189     @Override
190     public String toString()
191     {
192         return headers.toString();
193     }
194 
195     /**
196      * <p>A named list of string values.</p>
197      * <p>The name is case-sensitive and there must be at least one value.</p>
198      */
199     public static class Header
200     {
201         private final String name;
202         private final String[] values;
203 
204         private Header(String name, String value, String... values)
205         {
206             this.name = name;
207             this.values = new String[values.length + 1];
208             this.values[0] = value;
209             if (values.length > 0)
210                 System.arraycopy(values, 0, this.values, 1, values.length);
211         }
212 
213         @Override
214         public boolean equals(Object obj)
215         {
216             if (this == obj)
217                 return true;
218             if (obj == null || getClass() != obj.getClass())
219                 return false;
220             Header that = (Header)obj;
221             return name.equals(that.name) && Arrays.equals(values, that.values);
222         }
223 
224         @Override
225         public int hashCode()
226         {
227             int result = name.hashCode();
228             result = 31 * result + Arrays.hashCode(values);
229             return result;
230         }
231 
232         /**
233          * @return the header's name
234          */
235         public String name()
236         {
237             return name;
238         }
239 
240         /**
241          * @return the first header's value
242          */
243         public String value()
244         {
245             return values[0];
246         }
247 
248         /**
249          * <p>Attempts to convert the result of {@link #value()} to an integer,
250          * returning it if the conversion is successful; returns null if the
251          * result of {@link #value()} is null.</p>
252          *
253          * @return the result of {@link #value()} converted to an integer, or null
254          * @throws NumberFormatException if the conversion fails
255          */
256         public Integer valueAsInt()
257         {
258             final String value = value();
259             return value == null ? null : Integer.valueOf(value);
260         }
261 
262         /**
263          * @return the header's values
264          */
265         public String[] values()
266         {
267             return values;
268         }
269 
270         /**
271          * @return whether the header has multiple values
272          */
273         public boolean hasMultipleValues()
274         {
275             return values.length > 1;
276         }
277 
278         @Override
279         public String toString()
280         {
281             return Arrays.toString(values);
282         }
283     }
284 }