View Javadoc

1   /*******************************************************************************
2    * Copyright (c) 2011 Michael Mimo Moratti.
3    *  
4    * Michael Mimo Moratti licenses this file to you under the Apache License, version 2.0
5    * (the "License"); you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at:
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
11   * License for the specific language governing permissions and limitations
12   * under the License.
13   *******************************************************************************/
14  package ch.mimo.netty.handler.codec.icap;
15  
16  import java.util.LinkedHashSet;
17  import java.util.Map;
18  import java.util.Set;
19  
20  /**
21   * Icap Headers
22   * 
23   * This class provides a linked list implementation in order to store Icap headers.
24   * 
25   * @author Michael Mimo Moratti (mimo@mimo.ch)
26   *
27   */
28  // TODO add setDateheader format: "Fri, 20 May 2011 15:36:30 GMT"
29  public final class IcapHeaders {
30  	
31  	private Entry base;
32  	private Entry head;
33  	
34  	/**
35  	 * The most common Icap Header names.
36  	 * 
37  	 * @author Michael Mimo Moratti (mimo@mimo.ch)
38  	 *
39  	 */
40  	public static final class Names {
41          /**
42           * {@code "Cache-Control"}
43           */
44  		public static final String CACHE_CONTROL = "Cache-Control";
45          /**
46           * {@code "Connection"}
47           */
48  		public static final String CONNECTION = "Connection";
49          /**
50           * {@code "Date"}
51           */
52  		public static final String DATE = "Date";	
53          /**
54           * {@code "Expires"}
55           */
56  		public static final String EXPIRES = "Expires";
57          /**
58           * {@code "Pragma"}
59           */
60  		public static final String PRAGMA = "Pragma";
61          /**
62           * {@code "Trailer"}
63           */
64  		public static final String TRAILER = "Trailer";
65          /**
66           * {@code "Upgrade"}
67           */
68  		public static final String UPGRADE = "Upgrade";
69          /**
70           * {@code "Encapsulated"}
71           */
72  		public static final String ENCAPSULATED = "Encapsulated";
73          /**
74           * {@code "Authorization"}
75           */
76  		public static final String AUTHORIZATION = "Authorization";
77          /**
78           * {@code "Allow"}
79           */
80  		public static final String ALLOW = "Allow";
81          /**
82           * {@code "From"}
83           */
84  		public static final String FROM = "From";
85          /**
86           * {@code "Host"}
87           */
88  		public static final String HOST = "Host";
89          /**
90           * {@code "Referer"}
91           */
92  		public static final String REFERER = "Referer";
93          /**
94           * {@code "User-Agent"}
95           */
96  		public static final String USER_AGENT = "User-Agent";
97          /**
98           * {@code "Preview"}
99           */
100 		public static final String PREVIEW = "Preview";
101 		
102 		/**
103 		 * {@code "ISTag"}
104 		 */
105 		public static final String ISTAG = "ISTag";
106 	}
107 	
108 	public IcapHeaders() {
109 	}
110 	
111 	public void clearHeaders() {
112 		base = null;
113 		head = null;
114 	}
115 	
116 	/**
117 	 * Adds a header key,value combination to the list
118 	 * The existence of such a header name will have no impact. The header is simply added
119 	 * to the end of the linked list. 
120 	 * 
121 	 * @param name Icap message header name
122 	 * @param value Icap message header value. Can also be null
123 	 */
124 	public void addHeader(String name, Object value) {
125 		Entry entry = new Entry(name,value);
126 		if(base == null) {
127 			base = entry;
128 			head = entry;
129 		} else {
130 			Entry currentHead = head;
131 			head = entry;
132 			entry.before = currentHead;
133 			currentHead.after = entry;
134 		}
135 	}
136 	
137 	/**
138 	 * Sets one header at the end of the list.
139 	 * Headers with the same name that are already in the list will be removed first!
140 	 * 
141 	 * @param name Icap message header name
142 	 * @param value Icap message header value. Can also be null
143 	 */
144 	public void setHeader(String name, Object value) {
145 		removeHeader(name);
146 		addHeader(name,value);
147 	}
148 	
149 	/**
150 	 * Sets one header with many values.
151 	 * Headers with the same name that are already in the list will be removed first!
152 	 * 
153 	 * @param name Icap message header name
154 	 * @param values Icap message header value. Can also be null
155 	 */
156 	public void setHeader(String name, Iterable<?> values) {
157 		removeHeader(name);
158 		for(Object value : values) {
159 			addHeader(name,value);
160 		}
161 	}
162 	
163 	/**
164 	 * retrieves a header value from the list.
165 	 * If no header exists with the given name null is returned.
166 	 *
167 	 * If there are multiple headers with the same name only the first occurence in the
168 	 * list is returned.
169 	 * 
170 	 * @param name Icap message header name
171 	 * @return String value or null
172 	 */
173 	public String getHeader(String name) {
174 		Entry entry = base;
175 		while(entry != null) {
176 			if(identicalKeys(entry.getKey(),name)) {
177 				return entry.getValue();
178 			}
179 			entry = entry.after;
180 		}
181 		return null;
182 	}
183 	
184 	/**
185 	 * retrieves all values for one header name.
186 	 * If no header exists with that given name an empty set is returned.
187 	 * 
188 	 * @param name Icap message header name
189 	 * @return Set of values from all headers with the same name, or empty set.
190 	 */
191 	public Set<String> getHeaders(String name) {
192 		Set<String> values = new LinkedHashSet<String>();
193 		Entry entry = base;
194 		while(entry != null) {
195 			if(identicalKeys(entry.getKey(),name)) {
196 				values.add(entry.getValue());
197 			}
198 			entry = entry.after;
199 		}
200 		return values;
201 	}
202 	
203 	/**
204 	 * retrieval method for all headers that are currently in this list.
205 	 * 
206 	 * @return Set of Map Entry instances.
207 	 */
208 	public Set<Map.Entry<String, String>> getHeaders() {
209 		Set<Map.Entry<String, String>> headers = new LinkedHashSet<Map.Entry<String,String>>();
210 		Entry entry = base;
211 		while(entry != null) {
212 			headers.add(entry);
213 			entry = entry.after;
214 		}
215 		return headers;
216 	}
217 	
218 	/**
219 	 * check method to validate if a certain header does exists in the list.
220 	 * 
221 	 * @param name Icap message header name
222 	 * @return boolean true if the header exists.
223 	 */
224 	public boolean containsHeader(String name) {
225 		return getHeader(name) != null;
226 	}
227 	
228 	/**
229 	 * removes all headers with the same name from the list.
230 	 * 
231 	 * @param name Icap message header name
232 	 */
233 	public void removeHeader(String name) {
234 		if(base == null) {
235 			return;
236 		}
237 		Entry entry = null;
238 		if(base.after == null) {
239 			if(identicalKeys(base.getKey(),name)) {
240 				base = null;
241 				return;
242 			}
243 		} else {
244 			entry = base.after;
245 		}
246 		while(entry != null) {
247 			if(identicalKeys(entry.getKey(),name)) {
248 				Entry before = entry.before;
249 				Entry after = entry.after;
250 				before.after = after;
251 				after.before = before;
252 				entry = after;
253 			} else {
254 				entry = entry.after;
255 			}
256 		}
257 		if(identicalKeys(base.getKey(),name)) {
258 			base = base.after;
259 			base.before = null;
260 		}
261 	}
262 	
263 	/**
264 	 * retrieval method for all header names.
265 	 * this list is unique. If a header name has two entries the name is returned only once.
266 	 * 
267 	 * @return unique set with all header names in the list.
268 	 */
269 	public Set<String> getHeaderNames() {
270 		Set<String> names = new LinkedHashSet<String>();
271 		Entry entry = base;
272 		while(entry != null) {
273 			names.add(entry.getKey());
274 			entry = entry.after;
275 		}
276 		return names;
277 	}
278 	
279 	/**
280 	 * Convenience method to retrieve the @see {@link Integer} value from a Icap Preview header.
281 	 * If the header does not exist the value 0 is returned.
282 	 * If the header value cannot be parsed into a valid integer a @see {@link IcapDecodingError} is thrown.
283 	 * 
284 	 * @return int value of preview header.
285 	 */
286 	public int getPreviewHeaderValue() {
287 		String value = getHeader(Names.PREVIEW);
288 		int result = 0;
289 		try {
290 			if(value != null) {
291 				result = Integer.parseInt(value);
292 			}
293 		} catch(NumberFormatException nfe) {
294 			throw new IcapDecodingError("Unable to understand the preview amount value [" + value + "]");
295 		}
296 		return result;
297 	}
298 	
299 	private boolean identicalKeys(String key1, String key2) {
300 		if(key1 != null & key2 != null) {
301 			return key1.equalsIgnoreCase(key2);
302 		}
303 		return false;
304 	}
305 
306 	private static final class Entry implements Map.Entry<String, String> {
307 		
308 		private String key;
309 		private String value;
310 		
311 		private Entry before, after;
312 		
313 		Entry(String key, Object value) {
314 			IcapCodecUtil.validateHeaderName(key);
315 			this.key = key;
316 			if(value != null) {
317 				this.value = value.toString();
318 				IcapCodecUtil.validateHeaderValue(this.value);
319 			}
320 		}
321 		
322 		public String getKey() {
323 			return key;
324 		}
325 		
326 		public String getValue() {
327 			return value;
328 		}
329 		
330 		@Override
331 		public String setValue(String value) {
332 			return null;
333 		}
334 	}
335 	
336 	@Override
337 	public String toString() {
338 		StringBuilder builder = new StringBuilder();
339 		for(String name : getHeaderNames()) {
340 			builder.append("[" + name + "] = [" + getHeaders(name) + "]");
341 		}
342 		return builder.toString();
343 	}
344 }