1f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Licensed to the Apache Software Foundation (ASF) under one or more
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * contributor license agreements.  See the NOTICE file distributed with
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * this work for additional information regarding copyright ownership.
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The ASF licenses this file to You under the Apache License, Version 2.0
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * (the "License"); you may not use this file except in compliance with
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the License.  You may obtain a copy of the License at
8f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson *
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
10f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson *
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * See the License for the specific language governing permissions and
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * limitations under the License.
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage java.util.jar;
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException;
21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.InputStream;
22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.OutputStream;
2357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilsonimport java.nio.ByteBuffer;
24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.nio.CharBuffer;
2557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilsonimport java.nio.charset.CharsetEncoder;
2657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilsonimport java.nio.charset.CoderResult;
272a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughesimport java.nio.charset.StandardCharsets;
28adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.HashMap;
29adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Iterator;
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Map;
316186821cb13f4ac7ff50950c813394367e021eaeJesse Wilsonimport libcore.io.Streams;
32adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
33adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The {@code Manifest} class is used to obtain attribute information for a
35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * {@code JarFile} and its entries.
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic class Manifest implements Cloneable {
3857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    static final int LINE_LENGTH_LIMIT = 72;
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final byte[] LINE_SEPARATOR = new byte[] { '\r', '\n' };
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
4257995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    private static final byte[] VALUE_SEPARATOR = new byte[] { ':', ' ' };
4357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
441222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath    private final Attributes mainAttributes;
451222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath    private final HashMap<String, Attributes> entries;
46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
471222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath    static final class Chunk {
481222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath        final int start;
491222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath        final int end;
5057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
5157995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        Chunk(int start, int end) {
5257995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            this.start = start;
5357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            this.end = end;
5457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        }
5557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    }
5657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
5757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    private HashMap<String, Chunk> chunks;
58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
5957995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    /**
6057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     * The end of the main attributes section in the manifest is needed in
61adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * verification.
62adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
6357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    private int mainEnd;
64adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
65adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
66adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a new {@code Manifest} instance.
67adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
68adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Manifest() {
691222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath        entries = new HashMap<String, Attributes>();
701222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath        mainAttributes = new Attributes();
71adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
72adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
73adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
74adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a new {@code Manifest} instance using the attributes obtained
75adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * from the input stream.
7657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
77adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param is
78adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            {@code InputStream} to parse for attributes.
79adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
80adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if an IO error occurs while creating this {@code Manifest}
81adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
82adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Manifest(InputStream is) throws IOException {
831222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath        this();
841222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath        read(Streams.readFully(is));
85adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
86adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
87adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
88adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a new {@code Manifest} instance. The new instance will have the
89adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * same attributes as those found in the parameter {@code Manifest}.
9057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
91adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param man
92adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            {@code Manifest} instance to obtain attributes from.
93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
94adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @SuppressWarnings("unchecked")
95adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Manifest(Manifest man) {
96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        mainAttributes = (Attributes) man.mainAttributes.clone();
9757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        entries = (HashMap<String, Attributes>) ((HashMap<String, Attributes>) man
9857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson                .getEntries()).clone();
99adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
100adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1011222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath    Manifest(byte[] manifestBytes, boolean readChunks) throws IOException {
1021222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath        this();
103adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (readChunks) {
10457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            chunks = new HashMap<String, Chunk>();
105adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1061222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath        read(manifestBytes);
107adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
108adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
109adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
110adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Resets the both the main attributes as well as the entry attributes
111adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * associated with this {@code Manifest}.
112adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
113adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void clear() {
11457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        entries.clear();
115adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        mainAttributes.clear();
116adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
117adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
118adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
119adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the {@code Attributes} associated with the parameter entry
120adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * {@code name}.
12157995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
122adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param name
123adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the entry to obtain {@code Attributes} from.
124adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the Attributes for the entry or {@code null} if the entry does
125adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *         not exist.
126adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
127adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Attributes getAttributes(String name) {
128adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return getEntries().get(name);
129adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
130adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
131adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
132adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns a map containing the {@code Attributes} for each entry in the
133adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * {@code Manifest}.
13457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
135adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the map of entry attributes.
136adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
137adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Map<String, Attributes> getEntries() {
13857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        return entries;
13957995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    }
14057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
141adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
142adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the main {@code Attributes} of the {@code JarFile}.
14357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
144adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return main {@code Attributes} associated with the source {@code
145adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *         JarFile}.
146adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
147adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Attributes getMainAttributes() {
148adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return mainAttributes;
149adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
150adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
151adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
152adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a copy of this {@code Manifest}. The returned {@code Manifest}
153adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * will equal the {@code Manifest} from which it was cloned.
15457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
155adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return a copy of this instance.
156adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
157adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
158adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Object clone() {
159adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return new Manifest(this);
160adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
161adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
162adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
1633f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes     * Writes this {@code Manifest}'s name/attributes pairs to the given {@code OutputStream}.
1643f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes     * The {@code MANIFEST_VERSION} or {@code SIGNATURE_VERSION} attribute must be set before
1653f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes     * calling this method, or no attributes will be written.
16657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
167adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
168adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             If an error occurs writing the {@code Manifest}.
169adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
170adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void write(OutputStream os) throws IOException {
171adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        write(this, os);
172adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
173adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
174adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
175bfdb06dfa90bad70f55ae5c302a699fe28184b81Elliott Hughes     * Merges name/attribute pairs read from the input stream {@code is} into this manifest.
17657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
177adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param is
178adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            The {@code InputStream} to read from.
179adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
180bfdb06dfa90bad70f55ae5c302a699fe28184b81Elliott Hughes     *             If an error occurs reading the manifest.
181adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
182adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void read(InputStream is) throws IOException {
1831222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath        read(Streams.readFullyNoClose(is));
1841222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath    }
18557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
1861222b64d823e028efa6f02b08a55d788bfd57b16Narayan Kamath    private void read(byte[] buf) throws IOException {
18757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        if (buf.length == 0) {
18857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            return;
18957995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        }
19057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
1913a99bc391e224b8f1d371955c61dd47642e25b53Elliott Hughes        ManifestReader im = new ManifestReader(buf, mainAttributes);
1923a99bc391e224b8f1d371955c61dd47642e25b53Elliott Hughes        mainEnd = im.getEndOfMainSection();
1933a99bc391e224b8f1d371955c61dd47642e25b53Elliott Hughes        im.readEntries(entries, chunks);
19457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    }
19557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
196bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes    /**
197adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the hash code for this instance.
19857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
199adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return this {@code Manifest}'s hashCode.
200adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
201adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
202adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int hashCode() {
20357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        return mainAttributes.hashCode() ^ getEntries().hashCode();
204adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
205adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
206adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
207adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Determines if the receiver is equal to the parameter object. Two {@code
208adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Manifest}s are equal if they have identical main attributes as well as
209adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * identical entry attributes.
21057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
211adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param o
212adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the object to compare against.
213adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return {@code true} if the manifests are equal, {@code false} otherwise
214adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
215adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
216adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public boolean equals(Object o) {
217adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (o == null) {
218adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return false;
219adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
220adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (o.getClass() != this.getClass()) {
221adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return false;
222adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
223adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (!mainAttributes.equals(((Manifest) o).mainAttributes)) {
224adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return false;
225adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
22657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        return getEntries().equals(((Manifest) o).getEntries());
227adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
228adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
22957995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    Chunk getChunk(String name) {
230adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return chunks.get(name);
231adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
232adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
233adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    void removeChunks() {
234adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        chunks = null;
235adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
236adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
23757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    int getMainAttributesEnd() {
23857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        return mainEnd;
239adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
240adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
241adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
242adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Writes out the attribute information of the specified manifest to the
243adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * specified {@code OutputStream}
24457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
245adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param manifest
246adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the manifest to write out.
247adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param out
248adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            The {@code OutputStream} to write to.
249adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
250adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             If an error occurs writing the {@code Manifest}.
251adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
252adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    static void write(Manifest manifest, OutputStream out) throws IOException {
2532a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughes        CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
25456e742806ebb265e77de750cdb4575cff781fdf8Elliott Hughes        ByteBuffer buffer = ByteBuffer.allocate(LINE_LENGTH_LIMIT);
25557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
2563f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes        Attributes.Name versionName = Attributes.Name.MANIFEST_VERSION;
2573f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes        String version = manifest.mainAttributes.getValue(versionName);
2583f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes        if (version == null) {
2593f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes            versionName = Attributes.Name.SIGNATURE_VERSION;
2603f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes            version = manifest.mainAttributes.getValue(versionName);
2613f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes        }
262adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (version != null) {
2633f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes            writeEntry(out, versionName, version, encoder, buffer);
264adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            Iterator<?> entries = manifest.mainAttributes.keySet().iterator();
265adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            while (entries.hasNext()) {
266adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                Attributes.Name name = (Attributes.Name) entries.next();
2673f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes                if (!name.equals(versionName)) {
26856e742806ebb265e77de750cdb4575cff781fdf8Elliott Hughes                    writeEntry(out, name, manifest.mainAttributes.getValue(name), encoder, buffer);
269adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
270adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
271adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
272adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        out.write(LINE_SEPARATOR);
27357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        Iterator<String> i = manifest.getEntries().keySet().iterator();
274adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        while (i.hasNext()) {
275adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String key = i.next();
27659675dbb837c2a92352032e2ef0c8fc3305da9c8Elliott Hughes            writeEntry(out, Attributes.Name.NAME, key, encoder, buffer);
2773f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes            Attributes attributes = manifest.entries.get(key);
2783f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes            Iterator<?> entries = attributes.keySet().iterator();
279adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            while (entries.hasNext()) {
280adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                Attributes.Name name = (Attributes.Name) entries.next();
2813f52fe84a434dace741ba77de7322ca4d1d12fa7Elliott Hughes                writeEntry(out, name, attributes.getValue(name), encoder, buffer);
282adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
283adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            out.write(LINE_SEPARATOR);
284adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
285adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
286adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
28757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    private static void writeEntry(OutputStream os, Attributes.Name name,
288678e3d534e57c24e3a75a5153cc24714ebdaad8fJesse Wilson            String value, CharsetEncoder encoder, ByteBuffer bBuf) throws IOException {
289678e3d534e57c24e3a75a5153cc24714ebdaad8fJesse Wilson        String nameString = name.getName();
2902a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughes        os.write(nameString.getBytes(StandardCharsets.US_ASCII));
29157995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        os.write(VALUE_SEPARATOR);
29257995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
29357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        encoder.reset();
294678e3d534e57c24e3a75a5153cc24714ebdaad8fJesse Wilson        bBuf.clear().limit(LINE_LENGTH_LIMIT - nameString.length() - 2);
29557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
29657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        CharBuffer cBuf = CharBuffer.wrap(value);
29757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
29857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        while (true) {
299678e3d534e57c24e3a75a5153cc24714ebdaad8fJesse Wilson            CoderResult r = encoder.encode(cBuf, bBuf, true);
30057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            if (CoderResult.UNDERFLOW == r) {
30157995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson                r = encoder.flush(bBuf);
302adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
30357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            os.write(bBuf.array(), bBuf.arrayOffset(), bBuf.position());
304adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            os.write(LINE_SEPARATOR);
30557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            if (CoderResult.UNDERFLOW == r) {
30657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson                break;
30757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            }
30857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            os.write(' ');
30957995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            bBuf.clear().limit(LINE_LENGTH_LIMIT - 1);
310adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
311adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
312adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
313