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
20bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughesimport java.io.ByteArrayInputStream;
21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException;
22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.InputStream;
23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.OutputStream;
24bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughesimport java.lang.reflect.Field;
2557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilsonimport java.nio.ByteBuffer;
26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.nio.CharBuffer;
2757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilsonimport java.nio.charset.CharsetEncoder;
2856e742806ebb265e77de750cdb4575cff781fdf8Elliott Hughesimport java.nio.charset.Charsets;
2957995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilsonimport java.nio.charset.CoderResult;
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.HashMap;
31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Iterator;
32adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Map;
336186821cb13f4ac7ff50950c813394367e021eaeJesse Wilsonimport libcore.io.Streams;
34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The {@code Manifest} class is used to obtain attribute information for a
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * {@code JarFile} and its entries.
38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic class Manifest implements Cloneable {
4057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    static final int LINE_LENGTH_LIMIT = 72;
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final byte[] LINE_SEPARATOR = new byte[] { '\r', '\n' };
43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
4457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    private static final byte[] VALUE_SEPARATOR = new byte[] { ':', ' ' };
4557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
46bfdb06dfa90bad70f55ae5c302a699fe28184b81Elliott Hughes    private static final Attributes.Name NAME_ATTRIBUTE = new Attributes.Name("Name");
47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
48bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes    private static final Field BAIS_BUF = getByteArrayInputStreamField("buf");
49bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes    private static final Field BAIS_POS = getByteArrayInputStreamField("pos");
50bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes
51bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes    private static Field getByteArrayInputStreamField(String name) {
52bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes        try {
53bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes            Field f = ByteArrayInputStream.class.getDeclaredField(name);
54bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes            f.setAccessible(true);
55bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes            return f;
56bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes        } catch (Exception ex) {
57bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes            throw new AssertionError(ex);
58bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes        }
59bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes    }
60bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes
61adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private Attributes mainAttributes = new Attributes();
62adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
6357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    private HashMap<String, Attributes> entries = new HashMap<String, Attributes>();
6457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
6557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    static class Chunk {
6657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        int start;
6757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        int end;
6857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
6957995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        Chunk(int start, int end) {
7057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            this.start = start;
7157995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            this.end = end;
7257995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        }
7357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    }
7457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
7557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    private HashMap<String, Chunk> chunks;
76adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
7757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    /**
7857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     * The end of the main attributes section in the manifest is needed in
79adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * verification.
80adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
8157995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    private int mainEnd;
82adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
83adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
84adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a new {@code Manifest} instance.
85adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
86adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Manifest() {
87adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
88adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
89adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
90adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a new {@code Manifest} instance using the attributes obtained
91adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * from the input stream.
9257995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param is
94adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            {@code InputStream} to parse for attributes.
95adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if an IO error occurs while creating this {@code Manifest}
97adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
98adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Manifest(InputStream is) throws IOException {
99adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        read(is);
100adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
101adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
102adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
103adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a new {@code Manifest} instance. The new instance will have the
104adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * same attributes as those found in the parameter {@code Manifest}.
10557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
106adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param man
107adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            {@code Manifest} instance to obtain attributes from.
108adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
109adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @SuppressWarnings("unchecked")
110adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Manifest(Manifest man) {
111adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        mainAttributes = (Attributes) man.mainAttributes.clone();
11257995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        entries = (HashMap<String, Attributes>) ((HashMap<String, Attributes>) man
11357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson                .getEntries()).clone();
114adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
115adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
116adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    Manifest(InputStream is, boolean readChunks) throws IOException {
117adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (readChunks) {
11857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            chunks = new HashMap<String, Chunk>();
119adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
120adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        read(is);
121adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
122adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
123adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
124adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Resets the both the main attributes as well as the entry attributes
125adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * associated with this {@code Manifest}.
126adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
127adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void clear() {
12857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        entries.clear();
129adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        mainAttributes.clear();
130adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
131adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
132adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
133adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the {@code Attributes} associated with the parameter entry
134adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * {@code name}.
13557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
136adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param name
137adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the entry to obtain {@code Attributes} from.
138adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the Attributes for the entry or {@code null} if the entry does
139adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *         not exist.
140adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
141adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Attributes getAttributes(String name) {
142adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return getEntries().get(name);
143adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
144adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
145adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
146adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns a map containing the {@code Attributes} for each entry in the
147adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * {@code Manifest}.
14857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
149adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the map of entry attributes.
150adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
151adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Map<String, Attributes> getEntries() {
15257995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        return entries;
15357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    }
15457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
155adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
156adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the main {@code Attributes} of the {@code JarFile}.
15757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
158adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return main {@code Attributes} associated with the source {@code
159adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *         JarFile}.
160adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
161adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Attributes getMainAttributes() {
162adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return mainAttributes;
163adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
164adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
165adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
166adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a copy of this {@code Manifest}. The returned {@code Manifest}
167adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * will equal the {@code Manifest} from which it was cloned.
16857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
169adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return a copy of this instance.
170adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
171adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
172adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Object clone() {
173adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return new Manifest(this);
174adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
175adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
176adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
177adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Writes out the attribute information of the receiver to the specified
178adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * {@code OutputStream}.
17957995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
180adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param os
181adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            The {@code OutputStream} to write to.
182adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
183adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             If an error occurs writing the {@code Manifest}.
184adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
185adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void write(OutputStream os) throws IOException {
186adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        write(this, os);
187adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
188adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
189adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
190bfdb06dfa90bad70f55ae5c302a699fe28184b81Elliott Hughes     * Merges name/attribute pairs read from the input stream {@code is} into this manifest.
19157995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
192adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param is
193adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            The {@code InputStream} to read from.
194adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
195bfdb06dfa90bad70f55ae5c302a699fe28184b81Elliott Hughes     *             If an error occurs reading the manifest.
196adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
197adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void read(InputStream is) throws IOException {
19857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        byte[] buf;
199bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes        if (is instanceof ByteArrayInputStream) {
200bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes            buf = exposeByteArrayInputStreamBytes((ByteArrayInputStream) is);
201bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes        } else {
202df84cec32f3c1f71ea781e5f851724d84c35e620Jesse Wilson            buf = Streams.readFullyNoClose(is);
20357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        }
20457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
20557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        if (buf.length == 0) {
20657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            return;
20757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        }
20857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
20957995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        // a workaround for HARMONY-5662
21057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        // replace EOF and NUL with another new line
21157995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        // which does not trigger an error
21257995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        byte b = buf[buf.length - 1];
213b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (b == 0 || b == 26) {
21457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            buf[buf.length - 1] = '\n';
21557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        }
21657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
21757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        // Attributes.Name.MANIFEST_VERSION is not used for
21857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        // the second parameter for RI compatibility
219bfdb06dfa90bad70f55ae5c302a699fe28184b81Elliott Hughes        InitManifest im = new InitManifest(buf, mainAttributes, null);
22057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        mainEnd = im.getPos();
22157995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        im.initEntries(entries, chunks);
22257995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    }
22357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
224bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes    /**
225bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes     * Returns a byte[] containing all the bytes from a ByteArrayInputStream.
226bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes     * Where possible, this returns the actual array rather than a copy.
22757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     */
228bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes    private static byte[] exposeByteArrayInputStreamBytes(ByteArrayInputStream bais) {
229bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes        byte[] buffer;
230bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes        synchronized (bais) {
231bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes            byte[] buf;
232bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes            int pos;
233bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes            try {
234bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes                buf = (byte[]) BAIS_BUF.get(bais);
235bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes                pos = BAIS_POS.getInt(bais);
236bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes            } catch (IllegalAccessException iae) {
237bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes                throw new AssertionError(iae);
23857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            }
239bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes            int available = bais.available();
240bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes            if (pos == 0 && buf.length == available) {
241bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes                buffer = buf;
242bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes            } else {
243bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes                buffer = new byte[available];
244bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes                System.arraycopy(buf, pos, buffer, 0, available);
24557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            }
246bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes            bais.skip(available);
24757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        }
248bbf2c7d0462bb2c612e5a1a28e6d0ce5413d746dElliott Hughes        return buffer;
249adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
250adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
251adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
252adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the hash code for this instance.
25357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
254adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return this {@code Manifest}'s hashCode.
255adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
256adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
257adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int hashCode() {
25857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        return mainAttributes.hashCode() ^ getEntries().hashCode();
259adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
260adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
261adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
262adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Determines if the receiver is equal to the parameter object. Two {@code
263adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Manifest}s are equal if they have identical main attributes as well as
264adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * identical entry attributes.
26557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
266adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param o
267adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the object to compare against.
268adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return {@code true} if the manifests are equal, {@code false} otherwise
269adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
270adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
271adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public boolean equals(Object o) {
272adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (o == null) {
273adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return false;
274adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
275adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (o.getClass() != this.getClass()) {
276adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return false;
277adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
278adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (!mainAttributes.equals(((Manifest) o).mainAttributes)) {
279adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return false;
280adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
28157995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        return getEntries().equals(((Manifest) o).getEntries());
282adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
283adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
28457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    Chunk getChunk(String name) {
285adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return chunks.get(name);
286adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
287adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
288adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    void removeChunks() {
289adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        chunks = null;
290adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
291adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
29257995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    int getMainAttributesEnd() {
29357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        return mainEnd;
294adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
295adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
296adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
297adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Writes out the attribute information of the specified manifest to the
298adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * specified {@code OutputStream}
29957995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson     *
300adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param manifest
301adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the manifest to write out.
302adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param out
303adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            The {@code OutputStream} to write to.
304adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
305adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             If an error occurs writing the {@code Manifest}.
306adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
307adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    static void write(Manifest manifest, OutputStream out) throws IOException {
30856e742806ebb265e77de750cdb4575cff781fdf8Elliott Hughes        CharsetEncoder encoder = Charsets.UTF_8.newEncoder();
30956e742806ebb265e77de750cdb4575cff781fdf8Elliott Hughes        ByteBuffer buffer = ByteBuffer.allocate(LINE_LENGTH_LIMIT);
31057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
31156e742806ebb265e77de750cdb4575cff781fdf8Elliott Hughes        String version = manifest.mainAttributes.getValue(Attributes.Name.MANIFEST_VERSION);
312adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (version != null) {
31356e742806ebb265e77de750cdb4575cff781fdf8Elliott Hughes            writeEntry(out, Attributes.Name.MANIFEST_VERSION, version, encoder, buffer);
314adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            Iterator<?> entries = manifest.mainAttributes.keySet().iterator();
315adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            while (entries.hasNext()) {
316adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                Attributes.Name name = (Attributes.Name) entries.next();
317adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (!name.equals(Attributes.Name.MANIFEST_VERSION)) {
31856e742806ebb265e77de750cdb4575cff781fdf8Elliott Hughes                    writeEntry(out, name, manifest.mainAttributes.getValue(name), encoder, buffer);
319adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
320adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
321adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
322adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        out.write(LINE_SEPARATOR);
32357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        Iterator<String> i = manifest.getEntries().keySet().iterator();
324adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        while (i.hasNext()) {
325adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String key = i.next();
32657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            writeEntry(out, NAME_ATTRIBUTE, key, encoder, buffer);
32757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            Attributes attrib = manifest.entries.get(key);
328adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            Iterator<?> entries = attrib.keySet().iterator();
329adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            while (entries.hasNext()) {
330adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                Attributes.Name name = (Attributes.Name) entries.next();
33157995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson                writeEntry(out, name, attrib.getValue(name), encoder, buffer);
332adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
333adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            out.write(LINE_SEPARATOR);
334adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
335adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
336adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
33757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson    private static void writeEntry(OutputStream os, Attributes.Name name,
338678e3d534e57c24e3a75a5153cc24714ebdaad8fJesse Wilson            String value, CharsetEncoder encoder, ByteBuffer bBuf) throws IOException {
339678e3d534e57c24e3a75a5153cc24714ebdaad8fJesse Wilson        String nameString = name.getName();
340678e3d534e57c24e3a75a5153cc24714ebdaad8fJesse Wilson        os.write(nameString.getBytes(Charsets.US_ASCII));
34157995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        os.write(VALUE_SEPARATOR);
34257995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
34357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        encoder.reset();
344678e3d534e57c24e3a75a5153cc24714ebdaad8fJesse Wilson        bBuf.clear().limit(LINE_LENGTH_LIMIT - nameString.length() - 2);
34557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
34657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        CharBuffer cBuf = CharBuffer.wrap(value);
34757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson
34857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson        while (true) {
349678e3d534e57c24e3a75a5153cc24714ebdaad8fJesse Wilson            CoderResult r = encoder.encode(cBuf, bBuf, true);
35057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            if (CoderResult.UNDERFLOW == r) {
35157995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson                r = encoder.flush(bBuf);
352adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
35357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            os.write(bBuf.array(), bBuf.arrayOffset(), bBuf.position());
354adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            os.write(LINE_SEPARATOR);
35557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            if (CoderResult.UNDERFLOW == r) {
35657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson                break;
35757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            }
35857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            os.write(' ');
35957995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson            bBuf.clear().limit(LINE_LENGTH_LIMIT - 1);
360adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
361adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
362adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
363