1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package org.apache.harmony.luni.internal.net.www.protocol.http; 19 20import java.util.ArrayList; 21import java.util.Collections; 22import java.util.HashMap; 23import java.util.LinkedList; 24import java.util.List; 25import java.util.Map; 26import java.util.Map.Entry; 27 28/** 29 * The general structure for request / response header. It is essentially 30 * constructed by hashtable with key indexed in a vector for position lookup. 31 */ 32public class Header implements Cloneable { 33 /* 34 * we use the non-synchronized ArrayList and HashMap instead of the 35 * synchronized Vector and Hashtable 36 */ 37 private ArrayList<String> props; 38 39 private HashMap<String, LinkedList<String>> keyTable; 40 41 private String statusLine; 42 43 /** 44 * A generic header structure. Used mostly for request / response header. 45 * The key/value pair of the header may be inserted for later use. The key 46 * is stored in an array for indexed slot access. 47 */ 48 public Header() { 49 super(); 50 this.props = new ArrayList<String>(20); 51 this.keyTable = new HashMap<String, LinkedList<String>>(20); 52 } 53 54 /** 55 * The alternative constructor which sets the input map as its initial 56 * keyTable. 57 * 58 * @param map 59 * the initial keyTable as a map 60 */ 61 public Header(Map<String, List<String>> map) { 62 this(); // initialize fields 63 for (Entry<String, List<String>> next : map.entrySet()) { 64 String key = next.getKey(); 65 props.add(key); 66 List<String> value = next.getValue(); 67 LinkedList<String> linkedList = new LinkedList<String>(); 68 for (String element : value) { 69 linkedList.add(element); 70 props.add(element); 71 } 72 keyTable.put(key, linkedList); 73 } 74 } 75 76 @SuppressWarnings("unchecked") 77 @Override 78 public Object clone() { 79 try { 80 Header clone = (Header) super.clone(); 81 clone.props = (ArrayList<String>) props.clone(); 82 clone.keyTable = new HashMap<String, LinkedList<String>>(20); 83 for (Map.Entry<String, LinkedList<String>> next : this.keyTable 84 .entrySet()) { 85 LinkedList<String> v = (LinkedList<String>) next.getValue() 86 .clone(); 87 clone.keyTable.put(next.getKey(), v); 88 } 89 return clone; 90 } catch (CloneNotSupportedException e) { 91 throw new AssertionError(e); // android-changed 92 } 93 } 94 95 /** 96 * Add a field with the specified value. 97 * 98 * @param key 99 * @param value 100 */ 101 public void add(String key, String value) { 102 if (key == null) { 103 throw new NullPointerException(); 104 } 105 // BEGIN android-changed 106 key = key.toLowerCase(); 107 LinkedList<String> list = keyTable.get(key); 108 if (list == null) { 109 list = new LinkedList<String>(); 110 keyTable.put(key, list); 111 } 112 // END android-changed 113 list.add(value); 114 props.add(key); 115 props.add(value); 116 } 117 118 /** 119 * Set a field with the specified value. If the field is not found, it is 120 * added. If the field is found, the existing value(s) are overwritten. 121 * 122 * @param key 123 * @param value 124 */ 125 public void set(String key, String value) { 126 if (key == null) { 127 throw new NullPointerException(); 128 } 129 // BEGIN android-added 130 key = key.toLowerCase(); 131 // END android-added 132 LinkedList<String> list = keyTable.get(key); 133 if (list == null) { 134 add(key, value); 135 } else { 136 list.clear(); 137 list.add(value); 138 for (int i = 0; i < props.size(); i += 2) { 139 String propKey = props.get(i); 140 if (propKey != null && key.equals(propKey)) { 141 props.set(i + 1, value); 142 } 143 } 144 } 145 } 146 147 /** 148 * Provides an unmodifiable map with all String header names mapped to their 149 * String values. The map keys are Strings and the values are unmodifiable 150 * Lists of Strings. 151 * 152 * @return an unmodifiable map of the headers 153 * 154 * @since 1.4 155 */ 156 public Map<String, List<String>> getFieldMap() { 157 Map<String, List<String>> result = new HashMap<String, List<String>>( 158 keyTable.size()); 159 for (Map.Entry<String, LinkedList<String>> next : keyTable.entrySet()) { 160 List<String> v = next.getValue(); 161 result.put(next.getKey(), Collections.unmodifiableList(v)); 162 } 163 return Collections.unmodifiableMap(result); 164 } 165 166 /** 167 * Returns the element at <code>pos</code>, null if no such element 168 * exist. 169 * 170 * @return java.lang.String the value of the key 171 * @param pos 172 * int the position to look for 173 */ 174 public String get(int pos) { 175 if (pos >= 0 && pos < props.size() / 2) { 176 return props.get(pos * 2 + 1); 177 } 178 return null; 179 } 180 181 /** 182 * Returns the key of this header at <code>pos</code>, null if there are 183 * fewer keys in the header 184 * 185 * 186 * @return the key the desired position 187 * @param pos 188 * the position to look for 189 */ 190 public String getKey(int pos) { 191 if (pos >= 0 && pos < props.size() / 2) { 192 return props.get(pos * 2); 193 } 194 return null; 195 } 196 197 /** 198 * Returns the value corresponding to the specified key. 199 * 200 * @param key 201 * the key to look up. 202 * @return Answers the value for the given key, or <code>null</code> if no 203 * such key exists. 204 */ 205 public String get(String key) { 206 LinkedList<String> result = keyTable.get(key.toLowerCase()); 207 if (result == null) { 208 return null; 209 } 210 return result.getLast(); 211 } 212 213 /** 214 * Returns the number of keys stored in this header 215 * 216 * @return the number of keys. 217 */ 218 public int length() { 219 return props.size() / 2; 220 } 221 222 /** 223 * Sets the status line in the header request example: GET / HTTP/1.1 224 * response example: HTTP/1.1 200 OK 225 * 226 * @param statusLine 227 */ 228 public void setStatusLine(String statusLine) { 229 this.statusLine = statusLine; 230 /* 231 * we add the status line to the list of headers so that it is 232 * accessible from java.net.HttpURLConnection.getResponseCode() which 233 * calls 234 * org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnection.getHeaderField(0) 235 * to get it 236 */ 237 props.add(0, null); 238 props.add(1, statusLine); 239 } 240 241 /** 242 * Gets the status line in the header request example: GET / HTTP/1.1 243 * response example: HTTP/1.1 200 OK 244 * 245 * @return the status line 246 */ 247 public String getStatusLine() { 248 return statusLine; 249 } 250} 251