content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.framework.util.jar;
  
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Locale;
  import java.util.Map;
  import java.util.Set;
  
  /**
   * The {@code Attributes} class is used to store values for manifest entries.
   * Attribute keys are generally instances of {@code Attributes.Name}. Values
   * associated with attribute keys are of type {@code String}.
   */
  public class Attributes implements Cloneable, Map<Object, Object> {
  
      /**
       * The {@code Attributes} as name/value pairs. Maps the attribute names (as
       * {@link Attributes.Name}) of a JAR file manifest to arbitrary values. The
       * attribute names thus are obtained from the {@link Manifest} for
       * convenience.
       */
      protected Map<Object, Object> map;
  
      /**
       * The name part of the name/value pairs constituting an attribute as
       * defined by the specification of the JAR manifest. May be composed of the
       * following ASCII signs as defined in the EBNF below:
       *
       * <pre>
       * name       = alphanum *headerchar
       * headerchar = alphanum | - | _
       * alphanum   = {A-Z} | {a-z} | {0-9}
       * </pre>
       */
      public static class Name {
          private final String name;
  
          /** The class path (a main attribute). */
          public static final Name CLASS_PATH = new Name("Class-Path");
  
          /** The version of the manifest file (a main attribute). */
          public static final Name MANIFEST_VERSION = new Name("Manifest-Version");
  
          /** The main class's name (for stand-alone applications). */
          public static final Name MAIN_CLASS = new Name("Main-Class");
  
          /** Defines the signature version of the JAR file. */
          public static final Name SIGNATURE_VERSION = new Name("Signature-Version");
  
          /** The {@code Content-Type} manifest attribute. */
          public static final Name CONTENT_TYPE = new Name("Content-Type");
  
          /**
           * The {@code Sealed} manifest attribute which may have the value
           * {@code true} for sealed archives.
           */
          public static final Name SEALED = new Name("Sealed");
  
          /**
           * The {@code Implementation-Title} attribute whose value is a string
           * that defines the title of the extension implementation.
           */
          public static final Name IMPLEMENTATION_TITLE = new Name("Implementation-Title");
  
          /**
           * The {@code Implementation-Version} attribute defining the version of
           * the extension implementation.
           */
          public static final Name IMPLEMENTATION_VERSION = new Name("Implementation-Version");
  
          /**
           * The {@code Implementation-Vendor} attribute defining the organization
           * that maintains the extension implementation.
           */
          public static final Name IMPLEMENTATION_VENDOR = new Name("Implementation-Vendor");
  
          /**
           * The {@code Specification-Title} attribute defining the title of the
           * extension specification.
           */
          public static final Name SPECIFICATION_TITLE = new Name("Specification-Title");
  
          /**
           * The {@code Specification-Version} attribute defining the version of
           * the extension specification.
           */
          public static final Name SPECIFICATION_VERSION = new Name("Specification-Version");
  
          /**
           * The {@code Specification-Vendor} attribute defining the organization
           * that maintains the extension specification.
           */
          public static final Name SPECIFICATION_VENDOR = new Name("Specification-Vendor");
  
          /**
           * The {@code Extension-List} attribute defining the extensions that are
           * needed by the applet.
           */
          public static final Name EXTENSION_LIST = new Name("Extension-List");
  
          /**
           * The {@code Extension-Name} attribute which defines the unique name of
           * the extension.
           */
          public static final Name EXTENSION_NAME = new Name("Extension-Name");
  
          /**
           * The {@code Extension-Installation} attribute.
           */
          public static final Name EXTENSION_INSTALLATION = new Name("Extension-Installation");
  
          /**
           * The {@code Implementation-Vendor-Id} attribute specifies the vendor
           * of an extension implementation if the applet requires an
           * implementation from a specific vendor.
           */
          public static final Name IMPLEMENTATION_VENDOR_ID = new Name("Implementation-Vendor-Id");
  
          /**
           * The {@code Implementation-URL} attribute specifying a URL that can be
           * used to obtain the most recent version of the extension if the
           * required version is not already installed.
           */
          public static final Name IMPLEMENTATION_URL = new Name("Implementation-URL");
  
          static final Name NAME = new Name("Name");
  
          public Name(String name) {
              // encoded name + "\r\n" must be <= 72 bytes; ASCII-only so byte count equals char count
              if (name.isEmpty() || name.length() > Manifest.LINE_LENGTH_LIMIT - 2) {
                  throw new IllegalArgumentException(name);
              }
  
              for (int i = 0; i < name.length(); i++) {
                  char ch = name.charAt(i);
                  if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
                          || ch == '_' || ch == '-' || (ch >= '0' && ch <= '9'))) {
                      throw new IllegalArgumentException(name);
                  }
              }
  
              this.name = name;
          }
  
          String getName() {
              return name;
          }
  
          @Override public boolean equals(Object object) {
              return object instanceof Name
                      && ((Name) object).name.equalsIgnoreCase(name);
          }
  
          @Override public int hashCode() {
              return name.toLowerCase(Locale.US).hashCode();
          }
  
          @Override public String toString() {
              return name;
          }
      }
  
      /**
       * Constructs an {@code Attributes} instance.
       */
      public Attributes() {
          map = new HashMap<Object, Object>();
      }
  
      /**
       * Constructs an {@code Attributes} instance obtaining keys and values from
       * the parameter {@code attrib}.
       *
       * @param attrib
       *            The attributes to obtain entries from.
       */
      @SuppressWarnings("unchecked")
      public Attributes(Attributes attrib) {
          map = (Map<Object, Object>) ((HashMap) attrib.map).clone();
      }
  
      /**
       * Constructs an {@code Attributes} instance with initial capacity of size
       * {@code size}.
       *
       * @param size
       *            Initial size of this {@code Attributes} instance.
       */
      public Attributes(int size) {
          map = new HashMap<Object, Object>(size);
      }
  
      /**
       * Removes all key/value pairs from this {@code Attributes}.
       */
      public void clear() {
          map.clear();
      }
  
      /**
       * Determines whether this {@code Attributes} contains the specified key.
       *
       * @param key
       *            The key to search for.
       * @return {@code true} if the key is found, {@code false} otherwise.
       */
      public boolean containsKey(Object key) {
          return map.containsKey(key);
      }
  
      /**
       * Determines whether this {@code Attributes} contains the specified value.
       *
       * @param value
       *            the value to search for.
       * @return {@code true} if the value is found, {@code false} otherwise.
       */
      public boolean containsValue(Object value) {
          return map.containsValue(value);
      }
  
      /**
       * Returns a set containing map entries for each of the key/value pair
       * contained in this {@code Attributes}.
       *
       * @return a set of Map.Entry's
       */
      public Set<Map.Entry<Object, Object>> entrySet() {
          return map.entrySet();
      }
  
      /**
       * Returns the value associated with the parameter key.
       *
       * @param key
       *            the key to search for.
       * @return Object associated with key, or {@code null} if key does not
       *         exist.
       */
      public Object get(Object key) {
          return map.get(key);
      }
  
      /**
       * Determines whether this {@code Attributes} contains any keys.
       *
       * @return {@code true} if one or more keys exist, {@code false} otherwise.
       */
      public boolean isEmpty() {
          return map.isEmpty();
      }
  
      /**
       * Returns a {@code Set} containing all the keys found in this {@code
       * Attributes}.
       *
       * @return a {@code Set} of all keys.
       */
      public Set<Object> keySet() {
          return map.keySet();
      }
  
      /**
       * Stores key/value pairs in this {@code Attributes}.
       *
       * @param key
       *            the key to associate with value.
       * @param value
       *            the value to store in this {@code Attributes}.
       * @return the value being stored.
       * @exception ClassCastException
       *                when key is not an {@code Attributes.Name} or value is not
       *                a {@code String}.
       */
      @SuppressWarnings("cast")
      // Require cast to force ClassCastException
      public Object put(Object key, Object value) {
          return map.put((Name) key, (String) value);
      }
  
      /**
       * Stores all the key/value pairs in the argument in this {@code
       * Attributes}.
       *
       * @param attrib
       *            the associations to store (must be of type {@code
       *            Attributes}).
       */
      public void putAll(Map<?, ?> attrib) {
          if (attrib == null || !(attrib instanceof Attributes)) {
              throw new ClassCastException(attrib.getClass().getName() + " not an Attributes");
          }
          this.map.putAll(attrib);
      }
  
      /**
       * Deletes the key/value pair with key {@code key} from this {@code
       * Attributes}.
       *
       * @param key
       *            the key to remove.
       * @return the values associated with the removed key, {@code null} if not
       *         present.
       */
      public Object remove(Object key) {
          return map.remove(key);
      }
  
      /**
       * Returns the number of key/value pairs associated with this {@code
       * Attributes}.
       *
       * @return the size of this {@code Attributes}.
       */
      public int size() {
          return map.size();
      }
  
      /**
       * Returns a collection of all the values present in this {@code
       * Attributes}.
       *
       * @return a collection of all values present.
       */
      public Collection<Object> values() {
          return map.values();
      }
  
      @SuppressWarnings("unchecked")
      @Override
      public Object clone() {
          Attributes clone;
          try {
              clone = (Attributes) super.clone();
          } catch (CloneNotSupportedException e) {
              throw new AssertionError(e);
          }
          clone.map = (Map<Object, Object>) ((HashMap) map).clone();
          return clone;
      }
  
      /**
       * Returns the hash code of this {@code Attributes}.
       *
       * @return the hash code of this object.
       */
      @Override
      public int hashCode() {
          return map.hashCode();
      }
  
      /**
       * Determines if this {@code Attributes} and the parameter {@code
       * Attributes} are equal. Two {@code Attributes} instances are equal if they
       * contain the same keys and values.
       *
       * @param obj
       *            the object with which this {@code Attributes} is compared.
       * @return {@code true} if the {@code Attributes} are equal, {@code false}
       *         otherwise.
       */
      @Override
      public boolean equals(Object obj) {
          if (this == obj) {
              return true;
          }
          if (obj instanceof Attributes) {
              return map.equals(((Attributes) obj).map);
          }
          return false;
      }
  
      /**
       * Returns the value associated with the parameter {@code Attributes.Name}
       * key.
       *
       * @param name
       *            the key to obtain the value for.
       * @return the {@code String} associated with name, or {@code null} if name
       *         is not a valid key.
       */
      public String getValue(Attributes.Name name) {
          return (String) map.get(name);
      }
  
      /**
       * Returns the string associated with the parameter name.
       *
       * @param name
       *            the key to obtain the value for.
       * @return the string associated with name, or {@code null} if name is not a
       *         valid key.
       */
      public String getValue(String name) {
          return (String) map.get(new Attributes.Name(name));
      }
  
      /**
       * Stores the value {@code val} associated with the key {@code name} in this
       * {@code Attributes}.
       *
       * @param name
       *            the key to store.
       * @param val
       *            the value to store in this {@code Attributes}.
       * @return the value being stored.
       */
      public String putValue(String name, String val) {
          return (String) map.put(new Attributes.Name(name), val);
      }
  }
  