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