1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.multidex;
18
19import com.android.dx.cf.direct.DirectClassFile;
20import com.android.dx.cf.direct.StdAttributeFactory;
21import java.io.ByteArrayOutputStream;
22import java.io.File;
23import java.io.FileNotFoundException;
24import java.io.IOException;
25import java.io.InputStream;
26import java.util.ArrayList;
27import java.util.List;
28import java.util.regex.Pattern;
29import java.util.zip.ZipException;
30import java.util.zip.ZipFile;
31
32class Path {
33
34    static ClassPathElement getClassPathElement(File file)
35            throws ZipException, IOException {
36        if (file.isDirectory()) {
37            return new FolderPathElement(file);
38        } else if (file.isFile()) {
39            return new ArchivePathElement(new ZipFile(file));
40        } else if (file.exists()) {
41            throw new IOException("\"" + file.getPath() +
42                    "\" is not a directory neither a zip file");
43        } else {
44            throw new FileNotFoundException("File \"" + file.getPath() + "\" not found");
45        }
46    }
47
48    List<ClassPathElement> elements = new ArrayList<ClassPathElement>();
49    private final String definition;
50    private final ByteArrayOutputStream baos = new ByteArrayOutputStream(40 * 1024);
51    private final byte[] readBuffer = new byte[20 * 1024];
52
53    Path(String definition) throws IOException {
54        this.definition = definition;
55        for (String filePath : definition.split(Pattern.quote(File.pathSeparator))) {
56            try {
57                addElement(getClassPathElement(new File(filePath)));
58            } catch (IOException e) {
59                throw new IOException("Wrong classpath: " + e.getMessage(), e);
60            }
61        }
62    }
63
64    private static byte[] readStream(InputStream in, ByteArrayOutputStream baos, byte[] readBuffer)
65            throws IOException {
66        try {
67            for (;;) {
68                int amt = in.read(readBuffer);
69                if (amt < 0) {
70                    break;
71                }
72
73                baos.write(readBuffer, 0, amt);
74            }
75        } finally {
76            in.close();
77        }
78        return baos.toByteArray();
79    }
80
81    @Override
82    public String toString() {
83        return definition;
84    }
85
86    Iterable<ClassPathElement> getElements() {
87      return elements;
88    }
89
90    private void addElement(ClassPathElement element) {
91        assert element != null;
92        elements.add(element);
93    }
94
95    synchronized DirectClassFile getClass(String path) throws FileNotFoundException {
96        DirectClassFile classFile = null;
97        for (ClassPathElement element : elements) {
98            try {
99                InputStream in = element.open(path);
100                try {
101                    byte[] bytes = readStream(in, baos, readBuffer);
102                    baos.reset();
103                    classFile = new DirectClassFile(bytes, path, false);
104                    classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
105                    break;
106                } finally {
107                    in.close();
108                }
109            } catch (IOException e) {
110                // search next element
111            }
112        }
113        if (classFile == null) {
114            throw new FileNotFoundException("File \"" + path + "\" not found");
115        }
116        return classFile;
117    }
118}
119