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> &lt;extension&gt;-Extension-Name:
157         * unique name of the extension</li>
158         * <li> &lt;extension&gt;-Specification-Version:
159         * minimum specification version</li>
160         * <li> &lt;extension&gt;-Implementation-Version:
161         * minimum implementation version</li>
162         * <li> &lt;extension&gt;-Implementation-Vendor-Id:
163         * unique id of implementation vendor</li>
164         * <li> &lt;extension&gt;-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    }