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