GenerationalClassUtil.java revision 9784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3
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 e) {
84                L.d("cannot open zip file from %s", url);
85            } catch (URISyntaxException e) {
86                L.d("cannot open zip file from %s", url);
87            }
88        }
89    }
90
91    private static void loadFromDirectory(File directory) {
92        for (File file : FileUtils.listFiles(directory, TrueFileFilter.INSTANCE,
93                TrueFileFilter.INSTANCE)) {
94            for (ExtensionFilter filter : ExtensionFilter.values()) {
95                if (filter.accept(file.getName())) {
96                    InputStream inputStream = null;
97                    try {
98                        inputStream = FileUtils.openInputStream(file);
99                        Serializable item = fromInputStream(inputStream);
100                        if (item != null) {
101                            //noinspection unchecked
102                            sCache[filter.ordinal()].add(item);
103                            L.d("loaded item %s from file", item);
104                        }
105                    } catch (IOException e) {
106                        L.e(e, "Could not merge in Bindables from %s", file.getAbsolutePath());
107                    } catch (ClassNotFoundException e) {
108                        L.e(e, "Could not read Binding properties intermediate file. %s",
109                                file.getAbsolutePath());
110                    } finally {
111                        IOUtils.closeQuietly(inputStream);
112                    }
113                }
114            }
115        }
116    }
117
118    private static void loadFomZipFile(File file)
119            throws IOException {
120        ZipFile zipFile = new ZipFile(file);
121        Enumeration<? extends ZipEntry> entries = zipFile.entries();
122        while (entries.hasMoreElements()) {
123            ZipEntry entry = entries.nextElement();
124            for (ExtensionFilter filter : ExtensionFilter.values()) {
125                if (!filter.accept(entry.getName())) {
126                    continue;
127                }
128                InputStream inputStream = null;
129                try {
130                    inputStream = zipFile.getInputStream(entry);
131                    Serializable item = fromInputStream(inputStream);
132                    L.d("loaded item %s from zip file", item);
133                    if (item != null) {
134                        //noinspection unchecked
135                        sCache[filter.ordinal()].add(item);
136                    }
137                } catch (IOException e) {
138                    L.e(e, "Could not merge in Bindables from %s", file.getAbsolutePath());
139                } catch (ClassNotFoundException e) {
140                    L.e(e, "Could not read Binding properties intermediate file. %s",
141                            file.getAbsolutePath());
142                } finally {
143                    IOUtils.closeQuietly(inputStream);
144                }
145            }
146        }
147    }
148
149    private static Serializable fromInputStream(InputStream inputStream)
150            throws IOException, ClassNotFoundException {
151        ObjectInputStream in = new ObjectInputStream(inputStream);
152        return (Serializable) in.readObject();
153
154    }
155
156    public static void writeIntermediateFile(ProcessingEnvironment processingEnv,
157            String packageName, String fileName, Serializable object) {
158        ObjectOutputStream oos = null;
159        try {
160            FileObject intermediate = processingEnv.getFiler().createResource(
161                    StandardLocation.CLASS_OUTPUT, packageName,
162                    fileName);
163            OutputStream ios = intermediate.openOutputStream();
164            oos = new ObjectOutputStream(ios);
165            oos.writeObject(object);
166            oos.close();
167            L.d("wrote intermediate bindable file %s %s", packageName, fileName);
168        } catch (IOException e) {
169            L.e(e, "Could not write to intermediate file: %s", fileName);
170        } finally {
171            IOUtils.closeQuietly(oos);
172        }
173    }
174
175    public enum ExtensionFilter {
176        BR("-br.bin"),
177        LAYOUT("-layoutinfo.bin"),
178        SETTER_STORE("-setter_store.bin");
179        private final String mExtension;
180        ExtensionFilter(String extension) {
181            mExtension = extension;
182        }
183
184        public boolean accept(String entryName) {
185            return entryName.endsWith(mExtension);
186        }
187
188        public String getExtension() {
189            return mExtension;
190        }
191    }
192}
193