/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.databinding.tool.util; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.filefilter.TrueFileFilter; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import javax.annotation.processing.ProcessingEnvironment; import javax.tools.FileObject; import javax.tools.StandardLocation; /** * A utility class that helps adding build specific objects to the jar file * and their extraction later on. */ public class GenerationalClassUtil { private static List[] sCache = null; public static List loadObjects(ExtensionFilter filter) { if (sCache == null) { buildCache(); } //noinspection unchecked return sCache[filter.ordinal()]; } private static void buildCache() { L.d("building generational class cache"); ClassLoader classLoader = GenerationalClassUtil.class.getClassLoader(); Preconditions.check(classLoader instanceof URLClassLoader, "Class loader must be an" + "instance of URLClassLoader. %s", classLoader); //noinspection ConstantConditions final URLClassLoader urlClassLoader = (URLClassLoader) classLoader; sCache = new List[ExtensionFilter.values().length]; for (ExtensionFilter filter : ExtensionFilter.values()) { sCache[filter.ordinal()] = new ArrayList(); } for (URL url : urlClassLoader.getURLs()) { L.d("checking url %s for intermediate data", url); try { final File file = new File(url.toURI()); if (!file.exists()) { L.d("cannot load file for %s", url); continue; } if (file.isDirectory()) { // probably exported classes dir. loadFromDirectory(file); } else { // assume it is a zip file loadFomZipFile(file); } } catch (IOException | URISyntaxException e) { L.d("cannot open zip file from %s", url); } } } private static void loadFromDirectory(File directory) { for (File file : FileUtils.listFiles(directory, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)) { for (ExtensionFilter filter : ExtensionFilter.values()) { if (filter.accept(file.getName())) { InputStream inputStream = null; try { inputStream = FileUtils.openInputStream(file); Serializable item = fromInputStream(inputStream); if (item != null) { //noinspection unchecked sCache[filter.ordinal()].add(item); L.d("loaded item %s from file", item); } } catch (IOException e) { L.e(e, "Could not merge in Bindables from %s", file.getAbsolutePath()); } catch (ClassNotFoundException e) { L.e(e, "Could not read Binding properties intermediate file. %s", file.getAbsolutePath()); } finally { IOUtils.closeQuietly(inputStream); } } } } } private static void loadFomZipFile(File file) throws IOException { ZipFile zipFile = new ZipFile(file); Enumeration entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); for (ExtensionFilter filter : ExtensionFilter.values()) { if (!filter.accept(entry.getName())) { continue; } InputStream inputStream = null; try { inputStream = zipFile.getInputStream(entry); Serializable item = fromInputStream(inputStream); L.d("loaded item %s from zip file", item); if (item != null) { //noinspection unchecked sCache[filter.ordinal()].add(item); } } catch (IOException e) { L.e(e, "Could not merge in Bindables from %s", file.getAbsolutePath()); } catch (ClassNotFoundException e) { L.e(e, "Could not read Binding properties intermediate file. %s", file.getAbsolutePath()); } finally { IOUtils.closeQuietly(inputStream); } } } } private static Serializable fromInputStream(InputStream inputStream) throws IOException, ClassNotFoundException { ObjectInputStream in = new ObjectInputStream(inputStream); return (Serializable) in.readObject(); } public static void writeIntermediateFile(ProcessingEnvironment processingEnv, String packageName, String fileName, Serializable object) { ObjectOutputStream oos = null; try { FileObject intermediate = processingEnv.getFiler().createResource( StandardLocation.CLASS_OUTPUT, packageName, fileName); OutputStream ios = intermediate.openOutputStream(); oos = new ObjectOutputStream(ios); oos.writeObject(object); oos.close(); L.d("wrote intermediate bindable file %s %s", packageName, fileName); } catch (IOException e) { L.e(e, "Could not write to intermediate file: %s", fileName); } finally { IOUtils.closeQuietly(oos); } } public enum ExtensionFilter { BR("-br.bin"), LAYOUT("-layoutinfo.bin"), SETTER_STORE("-setter_store.bin"); private final String mExtension; ExtensionFilter(String extension) { mExtension = extension; } public boolean accept(String entryName) { return entryName.endsWith(mExtension); } public String getExtension() { return mExtension; } } }