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