001 /* Attributes.java -- Represents attribute name/value pairs from a Manifest 002 Copyright (C) 2000, 2002, 2005 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 package java.util.jar; 039 040 import gnu.java.util.jar.JarUtils; 041 042 import java.util.Collection; 043 import java.util.Hashtable; 044 import java.util.Map; 045 import java.util.Set; 046 047 /** 048 * Represents attribute name/value pairs from a Manifest as a Map. 049 * The names of an attribute are represented by the 050 * <code>Attributes.Name</code> class and should confirm to the restrictions 051 * described in that class. Note that the Map interface that Attributes 052 * implements allows you to put names and values into the attribute that don't 053 * follow these restriction (and are not really Atrribute.Names, but if you do 054 * that it might cause undefined behaviour later). 055 * <p> 056 * If you use the constants defined in the inner class Name then you can be 057 * sure that you always access the right attribute names. This makes 058 * manipulating the Attributes more or less type safe. 059 * <p> 060 * Most of the methods are wrappers to implement the Map interface. The really 061 * useful and often used methods are <code>getValue(Name)</code> and 062 * <code>getValue(String)</code>. If you actually want to set attributes you 063 * may want to use the <code>putValue(String, String)</code> method 064 * (sorry there is no public type safe <code>putValue(Name, String)</code> 065 * method). 066 * 067 * @see java.util.jar.Attributes.Name 068 * @author Mark Wielaard (mark@klomp.org) 069 */ 070 public class Attributes 071 implements Cloneable, Map<Object, Object> 072 { 073 074 // Fields 075 076 /** 077 * The map that holds all the attribute name/value pairs. In this 078 * implementation it is actually a Hashtable, but that can be different in 079 * other implementations. 080 */ 081 protected Map<Object, Object> map; 082 083 // Inner class 084 085 /** 086 * Represents a name of a Manifest Attribute. Defines a couple of well 087 * know names for the general main attributes, stand alone application 088 * attributes, applet attributes, extension identification attributes, 089 * package versioning and sealing attributes, file contents attributes, 090 * bean objects attribute and signing attributes. See the 091 * 092 * <p>The characters of a Name must obey the following restrictions:</p> 093 * 094 * <ul> 095 * <li>Must contain at least one character</li> 096 * <li>The first character must be alphanumeric (a-z, A-Z, 0-9)</li> 097 * <li>All other characters must be alphanumeric, a '-' or a '_'</li> 098 * </ul> 099 * 100 * <p>When comparing Names (with <code>equals</code>) all characters are 101 * converted to lowercase. But you can get the original case sensitive 102 * string with the <code>toString()</code> method.</p> 103 * 104 * <p>Most important attributes have a constant defined in this 105 * class. Some other attributes used in Manifest files are: 106 * <ul> 107 * <li> "Created-By" - General main attribute, tool and version 108 * that created this Manifest file.</li> 109 * <li> "Java-Bean" - Bean objects attribute, whether the entry is a Bean. 110 * Value is either "true" or "false".</li> 111 * <li> "Magic" - Signing attribute, application specific signing attribute. 112 * Must be understood by the manifest parser when present to validate the 113 * jar (entry).</li> 114 * </ul> 115 * 116 * @since 1.2 117 * @author Mark Wielaard (mark@klomp.org) 118 */ 119 public static class Name 120 { 121 // General Main Attributes 122 123 /** 124 * General main attribute - 125 * the version of this Manifest file. 126 */ 127 public static final Name MANIFEST_VERSION = new Name(JarUtils.MANIFEST_VERSION); 128 129 /** 130 * General main attribute - 131 * the version of the jar file signature. 132 */ 133 public static final Name SIGNATURE_VERSION = new Name(JarUtils.SIGNATURE_VERSION); 134 135 /** 136 * General main attribute - 137 * (relative) file paths of the libraries/classpaths that the Classes in 138 * this jar file depend on. Paths are separated by spaces. 139 */ 140 public static final Name CLASS_PATH = new Name("Class-Path"); 141 142 /** 143 * Stand alone application attribute - 144 * the entry (without the .class ending) that is the main 145 * class of this jar file. 146 */ 147 public static final Name MAIN_CLASS = new Name("Main-Class"); 148 149 /** 150 * Applet attribute - 151 * a list of extension libraries that the applet in this 152 * jar file depends on. 153 * For every named extension there should be some Attributes in the 154 * Manifest manifest file with the following Names: 155 * <ul> 156 * <li> <extension>-Extension-Name: 157 * unique name of the extension</li> 158 * <li> <extension>-Specification-Version: 159 * minimum specification version</li> 160 * <li> <extension>-Implementation-Version: 161 * minimum implementation version</li> 162 * <li> <extension>-Implementation-Vendor-Id: 163 * unique id of implementation vendor</li> 164 * <li> <extension>-Implementation-URL: 165 * where the latest version of the extension library can be found</li> 166 * </ul> 167 */ 168 public static final Name EXTENSION_LIST = new Name("Extension-List"); 169 170 /** 171 * Extension identification attribute - 172 * the name if the extension library contained in the jar. 173 */ 174 public static final Name EXTENSION_NAME = new Name("Extension-Name"); 175 176 /** 177 * Extension identification attribute - 178 * synonym for <code>EXTENSTION_NAME</code>. 179 */ 180 public static final Name EXTENSION_INSTALLATION = EXTENSION_NAME; 181 182 // Package versioning and sealing attributes 183 184 /** 185 * Package versioning - 186 * name of extension library contained in this jar. 187 */ 188 public static final Name IMPLEMENTATION_TITLE 189 = new Name("Implementation-Title"); 190 191 /** 192 * Package versioning - 193 * version of the extension library contained in this jar. 194 */ 195 public static final Name IMPLEMENTATION_VERSION 196 = new Name("Implementation-Version"); 197 198 /** 199 * Package versioning - 200 * name of extension library creator contained in this jar. 201 */ 202 public static final Name IMPLEMENTATION_VENDOR 203 = new Name("Implementation-Vendor"); 204 205 /** 206 * Package versioning - 207 * unique id of extension library creator. 208 */ 209 public static final Name IMPLEMENTATION_VENDOR_ID 210 = new Name("Implementation-Vendor-Id"); 211 212 /** 213 * Package versioning - 214 * location where this implementation can be downloaded. 215 */ 216 public static final Name IMPLEMENTATION_URL 217 = new Name("Implementation-URL"); 218 219 /** 220 * Package versioning - 221 * title of the specification contained in this jar. 222 */ 223 public static final Name SPECIFICATION_TITLE 224 = new Name("Specification-Title"); 225 226 /** 227 * Package versioning - 228 * version of the specification contained in this jar. 229 */ 230 public static final Name SPECIFICATION_VERSION 231 = new Name("Specification-Version"); 232 233 /** 234 * Package versioning - 235 * organisation that maintains the specification contains in this 236 * jar. 237 */ 238 public static final Name SPECIFICATION_VENDOR 239 = new Name("Specification-Vendor"); 240 241 /** 242 * Package sealing - 243 * whether (all) package(s) is(/are) sealed. Value is either "true" 244 * or "false". 245 */ 246 public static final Name SEALED = new Name("Sealed"); 247 248 /** 249 * File contents attribute - 250 * Mime type and subtype for the jar entry. 251 */ 252 public static final Name CONTENT_TYPE = new Name("Content-Type"); 253 254 /** The (lowercase) String representation of this Name */ 255 private final String name; 256 257 /** The original String given to the constructor */ 258 private final String origName; 259 260 // Constructor 261 262 /** 263 * Creates a new Name from the given String. 264 * Throws an IllegalArgumentException if the given String is empty or 265 * contains any illegal Name characters. 266 * 267 * @param name the name of the new Name 268 * @exception IllegalArgumentException if name isn't a valid String 269 * representation of a Name 270 * @exception NullPointerException if name is null 271 */ 272 public Name(String name) throws IllegalArgumentException, 273 NullPointerException 274 { 275 // name must not be null 276 // this will throw a NullPointerException if it is 277 char chars[] = name.toCharArray(); 278 279 // there must be at least one character 280 if (chars.length == 0) 281 throw new 282 IllegalArgumentException 283 ("There must be at least one character in a name"); 284 285 // first character must be alphanum 286 char c = chars[0]; 287 if (!((c >= 'a' && c <= 'z') || 288 (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))) 289 throw new 290 IllegalArgumentException("First character must be alphanum"); 291 292 // all other characters must be alphanums, '-' or '_' 293 for (int i = 1; i < chars.length; i++) 294 { 295 c = chars[i]; 296 if (!((c >= 'a' && c <= 'z') || 297 (c >= 'A' && c <= 'Z') || 298 (c >= '0' && c <= '9') || (c == '-') || (c == '_'))) 299 throw new 300 IllegalArgumentException 301 ("Characters must be alphanums, '-' or '_'"); 302 } 303 304 // Still here? Then convert to lower case and be done. 305 // Store the original name for toString(); 306 this.origName = name; 307 this.name = name.toLowerCase(); 308 } 309 310 /** 311 * Returns the hash code of the (lowercase) String representation of 312 * this Name. 313 */ 314 public int hashCode() 315 { 316 return name.hashCode(); 317 } 318 319 /** 320 * Checks if another object is equal to this Name object. 321 * Another object is equal to this Name object if it is an instance of 322 * Name and the (lowercase) string representation of the name is equal. 323 */ 324 public boolean equals(Object o) 325 { 326 // Quick and dirty check 327 if (name == o) 328 return true; 329 330 try 331 { 332 // Note that the constructor already converts the strings to 333 // lowercase. 334 String otherName = ((Name) o).name; 335 return name.equals(otherName); 336 } 337 catch (ClassCastException cce) 338 { 339 return false; 340 } 341 catch (NullPointerException npe) 342 { 343 return false; 344 } 345 } 346 347 /** 348 * Returns the string representation of this Name as given to the 349 * constructor (not neccesarily the lower case representation). 350 */ 351 public String toString() 352 { 353 return origName; 354 } 355 } 356 357 // Constructors 358 359 /** 360 * Creates an empty Attributes map. 361 */ 362 public Attributes() 363 { 364 map = new Hashtable(); 365 } 366 367 /** 368 * Creates an empty Attributes map with the given initial size. 369 * @param size the initial size of the underlying map 370 */ 371 public Attributes(int size) 372 { 373 map = new Hashtable(size); 374 } 375 376 /** 377 * Creates an Attributes map with the initial values taken from another 378 * Attributes map. 379 * @param attr Attributes map to take the initial values from 380 */ 381 public Attributes(Attributes attr) 382 { 383 map = new Hashtable(attr.map); 384 } 385 386 // Methods 387 388 /** 389 * Gets the value of an attribute name given as a String. 390 * 391 * @param name a String describing the Name to look for 392 * @return the value gotten from the map of null when not found 393 */ 394 public String getValue(String name) 395 { 396 return (String) get(new Name(name)); 397 } 398 399 /** 400 * Gets the value of the given attribute name. 401 * 402 * @param name the Name to look for 403 * @return the value gotten from the map of null when not found 404 */ 405 public String getValue(Name name) 406 { 407 return (String) get(name); 408 } 409 410 /** 411 * Stores an attribute name (represented by a String) and value in this 412 * Attributes map. 413 * When the (case insensitive string) name already exists the value is 414 * replaced and the old value is returned. 415 * 416 * @param name a (case insensitive) String representation of the attribite 417 * name to add/replace 418 * @param value the (new) value of the attribute name 419 * @returns the old value of the attribute name or null if it didn't exist 420 * yet 421 */ 422 public String putValue(String name, String value) 423 { 424 return putValue(new Name(name), value); 425 } 426 427 /** 428 * Stores an attribute name (represented by a String) and value in this 429 * Attributes map. 430 * When the name already exists the value is replaced and the old value 431 * is returned. 432 * 433 * @param name the attribite name to add/replace 434 * @param value the (new) value of the attribute name 435 * @returns the old value of the attribute name or null if it didn't exist 436 * yet 437 */ 438 private String putValue(Name name, String value) 439 { 440 return (String) put(name, value); 441 } 442 443 // Methods from Cloneable interface 444 445 /** 446 * Return a clone of this attribute map. 447 */ 448 public Object clone() 449 { 450 return new Attributes(this); 451 } 452 453 // Methods from Map interface 454 455 /** 456 * Removes all attributes. 457 */ 458 public void clear() 459 { 460 map.clear(); 461 } 462 463 /** 464 * Checks to see if there is an attribute with the specified name. 465 * XXX - what if the object is a String? 466 * 467 * @param attrName the name of the attribute to check 468 * @return true if there is an attribute with the specified name, false 469 * otherwise 470 */ 471 public boolean containsKey(Object attrName) 472 { 473 return map.containsKey(attrName); 474 } 475 476 /** 477 * Checks to see if there is an attribute name with the specified value. 478 * 479 * @param attrValue the value of a attribute to check 480 * @return true if there is an attribute name with the specified value, 481 * false otherwise 482 */ 483 public boolean containsValue(Object attrValue) 484 { 485 return map.containsValue(attrValue); 486 } 487 488 /** 489 * Gives a Set of attribute name and values pairs as MapEntries. 490 * @see java.util.Map.Entry 491 * @see java.util.Map#entrySet() 492 * 493 * @return a set of attribute name value pairs 494 */ 495 public Set<Map.Entry<Object, Object>> entrySet() 496 { 497 return map.entrySet(); 498 } 499 500 /** 501 * Checks to see if two Attributes are equal. The supplied object must be 502 * a real instance of Attributes and contain the same attribute name/value 503 * pairs. 504 * 505 * @param o another Attribute object which should be checked for equality 506 * @return true if the object is an instance of Attributes and contains the 507 * same name/value pairs, false otherwise 508 */ 509 public boolean equals(Object o) 510 { 511 // quick and dirty check 512 if (this == o) 513 return true; 514 515 try 516 { 517 return map.equals(((Attributes) o).map); 518 } 519 catch (ClassCastException cce) 520 { 521 return false; 522 } 523 catch (NullPointerException npe) 524 { 525 return false; 526 } 527 } 528 529 /** 530 * Gets the value of a specified attribute name. 531 * XXX - what if the object is a String? 532 * 533 * @param attrName the name of the attribute we want the value of 534 * @return the value of the specified attribute name or null when there is 535 * no such attribute name 536 */ 537 public Object get(Object attrName) 538 { 539 return map.get(attrName); 540 } 541 542 /** 543 * Returns the hashcode of the attribute name/value map. 544 */ 545 public int hashCode() 546 { 547 return map.hashCode(); 548 } 549 550 /** 551 * Returns true if there are no attributes set, false otherwise. 552 */ 553 public boolean isEmpty() 554 { 555 return map.isEmpty(); 556 } 557 558 /** 559 * Gives a Set of all the values of defined attribute names. 560 */ 561 public Set<Object> keySet() 562 { 563 return map.keySet(); 564 } 565 566 /** 567 * Adds or replaces a attribute name/value pair. 568 * XXX - What if the name is a string? What if the name is neither a Name 569 * nor a String? What if the value is not a string? 570 * 571 * @param name the name of the attribute 572 * @param value the (new) value of the attribute 573 * @return the old value of the attribute or null when there was no old 574 * attribute with this name 575 */ 576 public Object put(Object name, Object value) 577 { 578 return map.put(name, value); 579 } 580 581 /** 582 * Adds or replaces all attribute name/value pairs from another 583 * Attributes object to this one. The supplied Map must be an instance of 584 * Attributes. 585 * 586 * @param attr the Attributes object to merge with this one 587 * @exception ClassCastException if the supplied map is not an instance of 588 * Attributes 589 */ 590 public void putAll(Map<?, ?> attr) 591 { 592 if (!(attr instanceof Attributes)) 593 { 594 throw new 595 ClassCastException("Supplied Map is not an instance of Attributes"); 596 } 597 map.putAll(attr); 598 } 599 600 /** 601 * Remove a attribute name/value pair. 602 * XXX - What if the name is a String? 603 * 604 * @param name the name of the attribute name/value pair to remove 605 * @return the old value of the attribute or null if the attribute didn't 606 * exist 607 */ 608 public Object remove(Object name) 609 { 610 return map.remove(name); 611 } 612 613 /** 614 * Returns the number of defined attribute name/value pairs. 615 */ 616 public int size() 617 { 618 return map.size(); 619 } 620 621 /** 622 * Returns all the values of the defined attribute name/value pairs as a 623 * Collection. 624 */ 625 public Collection<Object> values() 626 { 627 return map.values(); 628 } 629 }