1a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar/*
2a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * Copyright (C) 2015 The Android Open Source Project
3a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar *
4a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * you may not use this file except in compliance with the License.
6a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * You may obtain a copy of the License at
7a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar *
8a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar *
10a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * Unless required by applicable law or agreed to in writing, software
11a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * See the License for the specific language governing permissions and
14a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * limitations under the License.
15a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar */
16a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
17fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.tool.util;
18a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
19a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport org.apache.commons.io.FileUtils;
20a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport org.apache.commons.io.IOUtils;
21a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport org.apache.commons.io.filefilter.TrueFileFilter;
22a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
23a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.io.File;
24a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.io.IOException;
25a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.io.InputStream;
26a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.io.ObjectInputStream;
27a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.io.ObjectOutputStream;
28a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.io.OutputStream;
29a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.io.Serializable;
30a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.net.URISyntaxException;
31a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.net.URL;
32a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.net.URLClassLoader;
33a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.ArrayList;
34a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.Enumeration;
35a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.List;
36a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.zip.ZipEntry;
37a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport java.util.zip.ZipFile;
38a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
39a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport javax.annotation.processing.ProcessingEnvironment;
40a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport javax.tools.FileObject;
41a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarimport javax.tools.StandardLocation;
42a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
43a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar/**
44a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * A utility class that helps adding build specific objects to the jar file
45a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar * and their extraction later on.
46a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar */
47a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyarpublic class GenerationalClassUtil {
48e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar    private static List[] sCache = null;
49e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar    public static <T extends Serializable> List<T> loadObjects(ExtensionFilter filter) {
50e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        if (sCache == null) {
51e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar            buildCache();
52a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
53e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        //noinspection unchecked
54e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        return sCache[filter.ordinal()];
55e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar    }
56e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar
57e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar    private static void buildCache() {
58e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        L.d("building generational class cache");
59e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        ClassLoader classLoader = GenerationalClassUtil.class.getClassLoader();
60e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        Preconditions.check(classLoader instanceof URLClassLoader, "Class loader must be an"
61e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                + "instance of URLClassLoader. %s", classLoader);
62e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        //noinspection ConstantConditions
63a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        final URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
64e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        sCache = new List[ExtensionFilter.values().length];
65e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        for (ExtensionFilter filter : ExtensionFilter.values()) {
66e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar            sCache[filter.ordinal()] = new ArrayList();
67e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        }
68a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        for (URL url : urlClassLoader.getURLs()) {
69a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            L.d("checking url %s for intermediate data", url);
70a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            try {
71a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                final File file = new File(url.toURI());
72a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                if (!file.exists()) {
73a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                    L.d("cannot load file for %s", url);
74a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                    continue;
75a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                }
76a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                if (file.isDirectory()) {
77a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                    // probably exported classes dir.
78e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    loadFromDirectory(file);
79a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                } else {
80a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                    // assume it is a zip file
81e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    loadFomZipFile(file);
82a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                }
839784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } catch (IOException e) {
849784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                L.d("cannot open zip file from %s", url);
859784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } catch (URISyntaxException e) {
86a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                L.d("cannot open zip file from %s", url);
87a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            }
88a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
89a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
90a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
91e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar    private static void loadFromDirectory(File directory) {
92e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        for (File file : FileUtils.listFiles(directory, TrueFileFilter.INSTANCE,
93e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                TrueFileFilter.INSTANCE)) {
94e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar            for (ExtensionFilter filter : ExtensionFilter.values()) {
95e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                if (filter.accept(file.getName())) {
96e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    InputStream inputStream = null;
97e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    try {
98e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                        inputStream = FileUtils.openInputStream(file);
99e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                        Serializable item = fromInputStream(inputStream);
100e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                        if (item != null) {
101e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                            //noinspection unchecked
102e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                            sCache[filter.ordinal()].add(item);
103e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                            L.d("loaded item %s from file", item);
104e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                        }
105e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    } catch (IOException e) {
106e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                        L.e(e, "Could not merge in Bindables from %s", file.getAbsolutePath());
107e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    } catch (ClassNotFoundException e) {
108e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                        L.e(e, "Could not read Binding properties intermediate file. %s",
109e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                                file.getAbsolutePath());
110e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    } finally {
111e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                        IOUtils.closeQuietly(inputStream);
112e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    }
113a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                }
114a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            }
115a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
116a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
117a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
118e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar    private static void loadFomZipFile(File file)
119e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar            throws IOException {
120a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        ZipFile zipFile = new ZipFile(file);
121a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        Enumeration<? extends ZipEntry> entries = zipFile.entries();
122a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        while (entries.hasMoreElements()) {
123a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            ZipEntry entry = entries.nextElement();
124e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar            for (ExtensionFilter filter : ExtensionFilter.values()) {
125e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                if (!filter.accept(entry.getName())) {
126e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    continue;
127e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                }
128e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                InputStream inputStream = null;
129e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                try {
130e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    inputStream = zipFile.getInputStream(entry);
131e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    Serializable item = fromInputStream(inputStream);
132e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    L.d("loaded item %s from zip file", item);
133e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    if (item != null) {
134e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                        //noinspection unchecked
135e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                        sCache[filter.ordinal()].add(item);
136e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    }
137e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                } catch (IOException e) {
138e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    L.e(e, "Could not merge in Bindables from %s", file.getAbsolutePath());
139e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                } catch (ClassNotFoundException e) {
140e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    L.e(e, "Could not read Binding properties intermediate file. %s",
141e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                            file.getAbsolutePath());
142e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                } finally {
143e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar                    IOUtils.closeQuietly(inputStream);
144a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                }
145a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            }
146a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
147a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
148a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
149e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar    private static Serializable fromInputStream(InputStream inputStream)
150e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar            throws IOException, ClassNotFoundException {
151a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        ObjectInputStream in = new ObjectInputStream(inputStream);
152e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        return (Serializable) in.readObject();
153a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
154a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
155a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
156a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    public static void writeIntermediateFile(ProcessingEnvironment processingEnv,
157a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            String packageName, String fileName, Serializable object) {
158a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        ObjectOutputStream oos = null;
159a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        try {
160a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            FileObject intermediate = processingEnv.getFiler().createResource(
161a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                    StandardLocation.CLASS_OUTPUT, packageName,
162a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar                    fileName);
163a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            OutputStream ios = intermediate.openOutputStream();
164a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            oos = new ObjectOutputStream(ios);
165a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            oos.writeObject(object);
166a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            oos.close();
167a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            L.d("wrote intermediate bindable file %s %s", packageName, fileName);
168a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        } catch (IOException e) {
169a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            L.e(e, "Could not write to intermediate file: %s", fileName);
170a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        } finally {
171a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            IOUtils.closeQuietly(oos);
172a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
173a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
174a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
175e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar    public enum ExtensionFilter {
176e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        BR("-br.bin"),
177e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        LAYOUT("-layoutinfo.bin"),
178e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        SETTER_STORE("-setter_store.bin");
179a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        private final String mExtension;
180e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        ExtensionFilter(String extension) {
181a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            mExtension = extension;
182a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
183a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar
184a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        public boolean accept(String entryName) {
185a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar            return entryName.endsWith(mExtension);
186a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar        }
187e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar
188e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        public String getExtension() {
189e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar            return mExtension;
190e8609ca3a9e95cb730d74f8a6114bc2ae11b6a10Yigit Boyar        }
191a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar    }
192a6e4583962e19e8e93b4ca3f9fe3d34560b6d96cYigit Boyar}
193