ProcessExpressions.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.annotationprocessor; 18 19import org.apache.commons.io.FileUtils; 20import org.apache.commons.io.IOUtils; 21import org.apache.commons.lang3.StringUtils; 22import org.apache.commons.lang3.SystemUtils; 23 24import android.databinding.BindingBuildInfo; 25import android.databinding.tool.CompilerChef; 26import android.databinding.tool.reflection.SdkUtil; 27import android.databinding.tool.store.ResourceBundle; 28import android.databinding.tool.util.GenerationalClassUtil; 29import android.databinding.tool.util.L; 30 31import java.io.File; 32import java.io.FilenameFilter; 33import java.io.IOException; 34import java.io.InputStream; 35import java.io.Serializable; 36import java.util.HashMap; 37import java.util.List; 38import java.util.Map; 39import java.util.Set; 40 41import javax.annotation.processing.ProcessingEnvironment; 42import javax.annotation.processing.RoundEnvironment; 43import javax.xml.bind.JAXBContext; 44import javax.xml.bind.JAXBException; 45import javax.xml.bind.Unmarshaller; 46 47public class ProcessExpressions extends ProcessDataBinding.ProcessingStep { 48 public ProcessExpressions() { 49 } 50 51 @Override 52 public boolean onHandleStep(RoundEnvironment roundEnvironment, 53 ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) { 54 ResourceBundle resourceBundle; 55 SdkUtil.initialize(buildInfo.minSdk(), new File(buildInfo.sdkRoot())); 56 resourceBundle = new ResourceBundle(buildInfo.modulePackage()); 57 List<Intermediate> intermediateList = 58 GenerationalClassUtil.loadObjects( 59 GenerationalClassUtil.ExtensionFilter.LAYOUT); 60 IntermediateV1 mine = createIntermediateFromLayouts(buildInfo.layoutInfoDir()); 61 if (mine != null) { 62 mine.removeOverridden(intermediateList); 63 intermediateList.add(mine); 64 saveIntermediate(processingEnvironment, buildInfo, mine); 65 } 66 // generate them here so that bindable parser can read 67 try { 68 generateBinders(resourceBundle, buildInfo, intermediateList); 69 } catch (Throwable t) { 70 L.e(t, "cannot generate view binders"); 71 } 72 return true; 73 } 74 75 private void saveIntermediate(ProcessingEnvironment processingEnvironment, 76 BindingBuildInfo buildInfo, IntermediateV1 intermediate) { 77 GenerationalClassUtil.writeIntermediateFile(processingEnvironment, 78 buildInfo.modulePackage(), buildInfo.modulePackage() + 79 GenerationalClassUtil.ExtensionFilter.LAYOUT.getExtension(), 80 intermediate); 81 } 82 83 @Override 84 public void onProcessingOver(RoundEnvironment roundEnvironment, 85 ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) { 86 } 87 88 private void generateBinders(ResourceBundle resourceBundle, BindingBuildInfo buildInfo, 89 List<Intermediate> intermediates) 90 throws Throwable { 91 for (Intermediate intermediate : intermediates) { 92 intermediate.appendTo(resourceBundle); 93 } 94 writeResourceBundle(resourceBundle, buildInfo.isLibrary(), buildInfo.minSdk(), 95 buildInfo.exportClassListTo()); 96 } 97 98 private IntermediateV1 createIntermediateFromLayouts(String layoutInfoFolderPath) { 99 final File layoutInfoFolder = new File(layoutInfoFolderPath); 100 if (!layoutInfoFolder.isDirectory()) { 101 L.d("layout info folder does not exist, skipping for %s", layoutInfoFolderPath); 102 return null; 103 } 104 IntermediateV1 result = new IntermediateV1(); 105 for (File layoutFile : layoutInfoFolder.listFiles(new FilenameFilter() { 106 @Override 107 public boolean accept(File dir, String name) { 108 return name.endsWith(".xml"); 109 } 110 })) { 111 try { 112 result.addEntry(layoutFile.getName(), FileUtils.readFileToString(layoutFile)); 113 } catch (IOException e) { 114 L.e(e, "cannot load layout file information. Try a clean build"); 115 } 116 } 117 return result; 118 } 119 120 private void writeResourceBundle(ResourceBundle resourceBundle, boolean forLibraryModule, 121 final int minSdk, String exportClassNamesTo) 122 throws JAXBException { 123 final CompilerChef compilerChef = CompilerChef.createChef(resourceBundle, getWriter()); 124 compilerChef.sealModels(); 125 compilerChef.writeComponent(); 126 if (compilerChef.hasAnythingToGenerate()) { 127 compilerChef.writeViewBinderInterfaces(forLibraryModule); 128 if (!forLibraryModule) { 129 compilerChef.writeViewBinders(minSdk); 130 } 131 } 132 if (forLibraryModule && exportClassNamesTo == null) { 133 L.e("When compiling a library module, build info must include exportClassListTo path"); 134 } 135 if (forLibraryModule) { 136 Set<String> classNames = compilerChef.getWrittenClassNames(); 137 String out = StringUtils.join(classNames, SystemUtils.LINE_SEPARATOR); 138 L.d("Writing list of classes to %s . \nList:%s", exportClassNamesTo, out); 139 try { 140 //noinspection ConstantConditions 141 FileUtils.write(new File(exportClassNamesTo), out); 142 } catch (IOException e) { 143 L.e(e, "Cannot create list of written classes"); 144 } 145 } 146 mCallback.onChefReady(compilerChef, forLibraryModule, minSdk); 147 } 148 149 public static interface Intermediate extends Serializable { 150 151 Intermediate upgrade(); 152 153 public void appendTo(ResourceBundle resourceBundle) throws Throwable; 154 } 155 156 public static class IntermediateV1 implements Intermediate { 157 158 transient Unmarshaller mUnmarshaller; 159 160 // name to xml content map 161 Map<String, String> mLayoutInfoMap = new HashMap<String, String>(); 162 163 @Override 164 public Intermediate upgrade() { 165 return this; 166 } 167 168 @Override 169 public void appendTo(ResourceBundle resourceBundle) throws JAXBException { 170 if (mUnmarshaller == null) { 171 JAXBContext context = JAXBContext 172 .newInstance(ResourceBundle.LayoutFileBundle.class); 173 mUnmarshaller = context.createUnmarshaller(); 174 } 175 for (String content : mLayoutInfoMap.values()) { 176 final InputStream is = IOUtils.toInputStream(content); 177 try { 178 final ResourceBundle.LayoutFileBundle bundle 179 = (ResourceBundle.LayoutFileBundle) mUnmarshaller.unmarshal(is); 180 resourceBundle.addLayoutBundle(bundle); 181 L.d("loaded layout info file %s", bundle); 182 } finally { 183 IOUtils.closeQuietly(is); 184 } 185 } 186 } 187 188 public void addEntry(String name, String contents) { 189 mLayoutInfoMap.put(name, contents); 190 } 191 192 public void removeOverridden(List<Intermediate> existing) { 193 // this is the way we get rid of files that are copied from previous modules 194 // it is important to do this before saving the intermediate file 195 for (Intermediate old : existing) { 196 if (old instanceof IntermediateV1) { 197 IntermediateV1 other = (IntermediateV1) old; 198 for (String key : other.mLayoutInfoMap.keySet()) { 199 // TODO we should consider the original file as the key here 200 // but aapt probably cannot provide that information 201 if (mLayoutInfoMap.remove(key) != null) { 202 L.d("removing %s from bundle because it came from another module", key); 203 } 204 } 205 } 206 } 207 } 208 } 209} 210