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 java.util.jar; 19 20import java.util.Collection; 21import java.util.HashMap; 22import java.util.Locale; 23import java.util.Map; 24import java.util.Set; 25 26/** 27 * The {@code Attributes} class is used to store values for manifest entries. 28 * Attribute keys are generally instances of {@code Attributes.Name}. Values 29 * associated with attribute keys are of type {@code String}. 30 */ 31public class Attributes implements Cloneable, Map<Object, Object> { 32 33 /** 34 * The {@code Attributes} as name/value pairs. Maps the attribute names (as 35 * {@link Attributes.Name}) of a JAR file manifest to arbitrary values. The 36 * attribute names thus are obtained from the {@link Manifest} for 37 * convenience. 38 */ 39 protected Map<Object, Object> map; 40 41 /** 42 * The name part of the name/value pairs constituting an attribute as 43 * defined by the specification of the JAR manifest. May be composed of the 44 * following ASCII signs as defined in the EBNF below: 45 * 46 * <pre> 47 * name = alphanum *headerchar 48 * headerchar = alphanum | - | _ 49 * alphanum = {A-Z} | {a-z} | {0-9} 50 * </pre> 51 */ 52 public static class Name { 53 private final String name; 54 55 /** The class path (a main attribute). */ 56 public static final Name CLASS_PATH = new Name("Class-Path"); 57 58 /** The version of the manifest file (a main attribute). */ 59 public static final Name MANIFEST_VERSION = new Name("Manifest-Version"); 60 61 /** The main class's name (for stand-alone applications). */ 62 public static final Name MAIN_CLASS = new Name("Main-Class"); 63 64 /** Defines the signature version of the JAR file. */ 65 public static final Name SIGNATURE_VERSION = new Name("Signature-Version"); 66 67 /** The {@code Content-Type} manifest attribute. */ 68 public static final Name CONTENT_TYPE = new Name("Content-Type"); 69 70 /** 71 * The {@code Sealed} manifest attribute which may have the value 72 * {@code true} for sealed archives. 73 */ 74 public static final Name SEALED = new Name("Sealed"); 75 76 /** 77 * The {@code Implementation-Title} attribute whose value is a string 78 * that defines the title of the extension implementation. 79 */ 80 public static final Name IMPLEMENTATION_TITLE = new Name("Implementation-Title"); 81 82 /** 83 * The {@code Implementation-Version} attribute defining the version of 84 * the extension implementation. 85 */ 86 public static final Name IMPLEMENTATION_VERSION = new Name("Implementation-Version"); 87 88 /** 89 * The {@code Implementation-Vendor} attribute defining the organization 90 * that maintains the extension implementation. 91 */ 92 public static final Name IMPLEMENTATION_VENDOR = new Name("Implementation-Vendor"); 93 94 /** 95 * The {@code Specification-Title} attribute defining the title of the 96 * extension specification. 97 */ 98 public static final Name SPECIFICATION_TITLE = new Name("Specification-Title"); 99 100 /** 101 * The {@code Specification-Version} attribute defining the version of 102 * the extension specification. 103 */ 104 public static final Name SPECIFICATION_VERSION = new Name("Specification-Version"); 105 106 /** 107 * The {@code Specification-Vendor} attribute defining the organization 108 * that maintains the extension specification. 109 */ 110 public static final Name SPECIFICATION_VENDOR = new Name("Specification-Vendor"); 111 112 /** 113 * The {@code Extension-List} attribute defining the extensions that are 114 * needed by the applet. 115 */ 116 public static final Name EXTENSION_LIST = new Name("Extension-List"); 117 118 /** 119 * The {@code Extension-Name} attribute which defines the unique name of 120 * the extension. 121 */ 122 public static final Name EXTENSION_NAME = new Name("Extension-Name"); 123 124 /** 125 * The {@code Extension-Installation} attribute. 126 */ 127 public static final Name EXTENSION_INSTALLATION = new Name("Extension-Installation"); 128 129 /** 130 * The {@code Implementation-Vendor-Id} attribute specifies the vendor 131 * of an extension implementation if the applet requires an 132 * implementation from a specific vendor. 133 */ 134 public static final Name IMPLEMENTATION_VENDOR_ID = new Name("Implementation-Vendor-Id"); 135 136 /** 137 * The {@code Implementation-URL} attribute specifying a URL that can be 138 * used to obtain the most recent version of the extension if the 139 * required version is not already installed. 140 */ 141 public static final Name IMPLEMENTATION_URL = new Name("Implementation-URL"); 142 143 static final Name NAME = new Name("Name"); 144 145 public Name(String name) { 146 // encoded name + "\r\n" must be <= 72 bytes; ASCII-only so byte count equals char count 147 if (name.isEmpty() || name.length() > Manifest.LINE_LENGTH_LIMIT - 2) { 148 throw new IllegalArgumentException(name); 149 } 150 151 for (int i = 0; i < name.length(); i++) { 152 char ch = name.charAt(i); 153 if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') 154 || ch == '_' || ch == '-' || (ch >= '0' && ch <= '9'))) { 155 throw new IllegalArgumentException(name); 156 } 157 } 158 159 this.name = name; 160 } 161 162 String getName() { 163 return name; 164 } 165 166 @Override public boolean equals(Object object) { 167 return object instanceof Name 168 && ((Name) object).name.equalsIgnoreCase(name); 169 } 170 171 @Override public int hashCode() { 172 return name.toLowerCase(Locale.US).hashCode(); 173 } 174 175 @Override public String toString() { 176 return name; 177 } 178 } 179 180 /** 181 * Constructs an {@code Attributes} instance. 182 */ 183 public Attributes() { 184 map = new HashMap<Object, Object>(); 185 } 186 187 /** 188 * Constructs an {@code Attributes} instance obtaining keys and values from 189 * the parameter {@code attrib}. 190 * 191 * @param attrib 192 * The attributes to obtain entries from. 193 */ 194 @SuppressWarnings("unchecked") 195 public Attributes(Attributes attrib) { 196 map = (Map<Object, Object>) ((HashMap) attrib.map).clone(); 197 } 198 199 /** 200 * Constructs an {@code Attributes} instance with initial capacity of size 201 * {@code size}. 202 * 203 * @param size 204 * Initial size of this {@code Attributes} instance. 205 */ 206 public Attributes(int size) { 207 map = new HashMap<Object, Object>(size); 208 } 209 210 /** 211 * Removes all key/value pairs from this {@code Attributes}. 212 */ 213 public void clear() { 214 map.clear(); 215 } 216 217 /** 218 * Determines whether this {@code Attributes} contains the specified key. 219 * 220 * @param key 221 * The key to search for. 222 * @return {@code true} if the key is found, {@code false} otherwise. 223 */ 224 public boolean containsKey(Object key) { 225 return map.containsKey(key); 226 } 227 228 /** 229 * Determines whether this {@code Attributes} contains the specified value. 230 * 231 * @param value 232 * the value to search for. 233 * @return {@code true} if the value is found, {@code false} otherwise. 234 */ 235 public boolean containsValue(Object value) { 236 return map.containsValue(value); 237 } 238 239 /** 240 * Returns a set containing map entries for each of the key/value pair 241 * contained in this {@code Attributes}. 242 * 243 * @return a set of Map.Entry's 244 */ 245 public Set<Map.Entry<Object, Object>> entrySet() { 246 return map.entrySet(); 247 } 248 249 /** 250 * Returns the value associated with the parameter key. 251 * 252 * @param key 253 * the key to search for. 254 * @return Object associated with key, or {@code null} if key does not 255 * exist. 256 */ 257 public Object get(Object key) { 258 return map.get(key); 259 } 260 261 /** 262 * Determines whether this {@code Attributes} contains any keys. 263 * 264 * @return {@code true} if one or more keys exist, {@code false} otherwise. 265 */ 266 public boolean isEmpty() { 267 return map.isEmpty(); 268 } 269 270 /** 271 * Returns a {@code Set} containing all the keys found in this {@code 272 * Attributes}. 273 * 274 * @return a {@code Set} of all keys. 275 */ 276 public Set<Object> keySet() { 277 return map.keySet(); 278 } 279 280 /** 281 * Stores key/value pairs in this {@code Attributes}. 282 * 283 * @param key 284 * the key to associate with value. 285 * @param value 286 * the value to store in this {@code Attributes}. 287 * @return the value being stored. 288 * @exception ClassCastException 289 * when key is not an {@code Attributes.Name} or value is not 290 * a {@code String}. 291 */ 292 @SuppressWarnings("cast") 293 // Require cast to force ClassCastException 294 public Object put(Object key, Object value) { 295 return map.put((Name) key, (String) value); 296 } 297 298 /** 299 * Stores all the key/value pairs in the argument in this {@code 300 * Attributes}. 301 * 302 * @param attrib 303 * the associations to store (must be of type {@code 304 * Attributes}). 305 */ 306 public void putAll(Map<?, ?> attrib) { 307 if (attrib == null || !(attrib instanceof Attributes)) { 308 throw new ClassCastException(attrib.getClass().getName() + " not an Attributes"); 309 } 310 this.map.putAll(attrib); 311 } 312 313 /** 314 * Deletes the key/value pair with key {@code key} from this {@code 315 * Attributes}. 316 * 317 * @param key 318 * the key to remove. 319 * @return the values associated with the removed key, {@code null} if not 320 * present. 321 */ 322 public Object remove(Object key) { 323 return map.remove(key); 324 } 325 326 /** 327 * Returns the number of key/value pairs associated with this {@code 328 * Attributes}. 329 * 330 * @return the size of this {@code Attributes}. 331 */ 332 public int size() { 333 return map.size(); 334 } 335 336 /** 337 * Returns a collection of all the values present in this {@code 338 * Attributes}. 339 * 340 * @return a collection of all values present. 341 */ 342 public Collection<Object> values() { 343 return map.values(); 344 } 345 346 @SuppressWarnings("unchecked") 347 @Override 348 public Object clone() { 349 Attributes clone; 350 try { 351 clone = (Attributes) super.clone(); 352 } catch (CloneNotSupportedException e) { 353 throw new AssertionError(e); 354 } 355 clone.map = (Map<Object, Object>) ((HashMap) map).clone(); 356 return clone; 357 } 358 359 /** 360 * Returns the hash code of this {@code Attributes}. 361 * 362 * @return the hash code of this object. 363 */ 364 @Override 365 public int hashCode() { 366 return map.hashCode(); 367 } 368 369 /** 370 * Determines if this {@code Attributes} and the parameter {@code 371 * Attributes} are equal. Two {@code Attributes} instances are equal if they 372 * contain the same keys and values. 373 * 374 * @param obj 375 * the object with which this {@code Attributes} is compared. 376 * @return {@code true} if the {@code Attributes} are equal, {@code false} 377 * otherwise. 378 */ 379 @Override 380 public boolean equals(Object obj) { 381 if (this == obj) { 382 return true; 383 } 384 if (obj instanceof Attributes) { 385 return map.equals(((Attributes) obj).map); 386 } 387 return false; 388 } 389 390 /** 391 * Returns the value associated with the parameter {@code Attributes.Name} 392 * key. 393 * 394 * @param name 395 * the key to obtain the value for. 396 * @return the {@code String} associated with name, or {@code null} if name 397 * is not a valid key. 398 */ 399 public String getValue(Attributes.Name name) { 400 return (String) map.get(name); 401 } 402 403 /** 404 * Returns the string associated with the parameter name. 405 * 406 * @param name 407 * the key to obtain the value for. 408 * @return the string associated with name, or {@code null} if name is not a 409 * valid key. 410 */ 411 public String getValue(String name) { 412 return (String) map.get(new Attributes.Name(name)); 413 } 414 415 /** 416 * Stores the value {@code val} associated with the key {@code name} in this 417 * {@code Attributes}. 418 * 419 * @param name 420 * the key to store. 421 * @param val 422 * the value to store in this {@code Attributes}. 423 * @return the value being stored. 424 */ 425 public String putValue(String name, String val) { 426 return (String) map.put(new Attributes.Name(name), val); 427 } 428} 429