diff options
Diffstat (limited to 'compiler')
8 files changed, 223 insertions, 29 deletions
diff --git a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java index 7252dcbf..8382d127 100644 --- a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java +++ b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java @@ -73,7 +73,7 @@ public class ProcessDataBinding extends AbstractProcessor { private boolean doProcess(RoundEnvironment roundEnv) { if (mProcessingSteps == null) { readArguments(); - initProcessingSteps(); + initProcessingSteps(processingEnv); } if (mCompilerArgs == null) { return false; @@ -108,7 +108,7 @@ public class ProcessDataBinding extends AbstractProcessor { return SourceVersion.latest(); } - private void initProcessingSteps() { + private void initProcessingSteps(ProcessingEnvironment processingEnv) { final ProcessBindable processBindable = new ProcessBindable(); mProcessingSteps = Arrays.asList( new ProcessMethodAdapters(), @@ -141,7 +141,8 @@ public class ProcessDataBinding extends AbstractProcessor { return; } mWrittenMapper = true; - mChef.writeDataBinderMapper(mCompilerArgs, mBRVariableLookup, mModulePackages); + mChef.writeDataBinderMapper(processingEnv, mCompilerArgs, mBRVariableLookup, + mModulePackages); } @Override diff --git a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java index 1006f335..d8b9bbef 100644 --- a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java +++ b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java @@ -17,11 +17,11 @@ package android.databinding.annotationprocessor; import android.databinding.tool.CompilerChef; +import android.databinding.tool.Context; import android.databinding.tool.DataBindingCompilerArgs; import android.databinding.tool.LayoutXmlProcessor; import android.databinding.tool.processing.Scope; import android.databinding.tool.processing.ScopedException; -import android.databinding.tool.reflection.SdkUtil; import android.databinding.tool.store.GenClassInfoLog; import android.databinding.tool.store.ResourceBundle; import android.databinding.tool.util.GenerationalClassUtil; @@ -29,9 +29,13 @@ import android.databinding.tool.util.L; import android.databinding.tool.util.LoggedErrorException; import android.databinding.tool.util.Preconditions; import android.databinding.tool.util.StringUtils; +import android.databinding.tool.writer.BindingMapperWriter; +import android.databinding.tool.writer.BindingMapperWriterV2; +import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.google.common.base.Joiner; +import com.google.common.base.Predicate; import org.apache.commons.io.Charsets; import org.apache.commons.io.FileUtils; @@ -50,11 +54,13 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.TypeElement; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; @@ -72,6 +78,8 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep { resourceBundle = new ResourceBundle(args.getModulePackage()); final List<IntermediateV2> intermediateList; GenClassInfoLog infoLog = null; + @Nullable + CompilerChef v1CompatChef = null; if (args.isEnableV2()) { try { infoLog = ResourceBundle.loadClassInfoFromFolder( @@ -83,6 +91,12 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep { } resourceBundle.addDependencyLayouts(infoLog); intermediateList = Collections.emptyList(); + v1CompatChef = new ProcessExpressionsFromV1Compat( + processingEnvironment, + args, + loadDependencyIntermediates(), + getWriter() + ).generate(); } else { intermediateList = loadDependencyIntermediates(); for (Intermediate intermediate : intermediateList) { @@ -109,7 +123,7 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep { } // generate them here so that bindable parser can read try { - writeResourceBundle(resourceBundle, args, infoLog); + writeResourceBundle(resourceBundle, args, infoLog, v1CompatChef); } catch (Throwable t) { L.e(t, "cannot generate view binders"); } @@ -210,9 +224,11 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep { private void writeResourceBundle( ResourceBundle resourceBundle, DataBindingCompilerArgs compilerArgs, - @Nullable GenClassInfoLog classInfoLog) { + @Nullable GenClassInfoLog classInfoLog, + @NonNull CompilerChef v1CompatChef) { final CompilerChef compilerChef = CompilerChef.createChef(resourceBundle, getWriter(), compilerArgs); + compilerChef.setV1CompatChef(v1CompatChef); compilerChef.sealModels(); // write this only if we are compiling an app or a library test app. // even if data binding is enabled for tests, we should not re-generate this. @@ -237,6 +253,10 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep { } if (compilerArgs.isLibrary() && !compilerArgs.isTestVariant()) { Set<String> classNames = compilerChef.getClassesToBeStripped(); + if (v1CompatChef != null) { + classNames.addAll(v1CompatChef.getClassesToBeStripped()); + classNames.add(BindingMapperWriter.V1_COMPAT_QNAME); + } String out = Joiner.on(StringUtils.LINE_SEPARATOR).join(classNames); L.d("Writing list of classes to %s . \nList:%s", compilerArgs.getExportClassListTo(), out); @@ -259,8 +279,6 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep { public static class IntermediateV1 implements Intermediate { - transient Unmarshaller mUnmarshaller; - // name to xml content map Map<String, String> mLayoutInfoMap = new HashMap<String, String>(); @@ -268,29 +286,31 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep { public Intermediate upgrade() { final IntermediateV2 updated = new IntermediateV2(); updated.mLayoutInfoMap = mLayoutInfoMap; - updated.mUnmarshaller = mUnmarshaller; return updated; } @Override public void appendTo(ResourceBundle resourceBundle, boolean fromSource) throws JAXBException { - if (mUnmarshaller == null) { - JAXBContext context = JAXBContext - .newInstance(ResourceBundle.LayoutFileBundle.class); - mUnmarshaller = context.createUnmarshaller(); - } + extractBundles().forEach(layoutFileBundle -> { + resourceBundle.addLayoutBundle(layoutFileBundle, fromSource); + }); + } + + public List<ResourceBundle.LayoutFileBundle> extractBundles() throws JAXBException { + List<ResourceBundle.LayoutFileBundle> bundles = new ArrayList<>(); for (String content : mLayoutInfoMap.values()) { final InputStream is = IOUtils.toInputStream(content); try { - final ResourceBundle.LayoutFileBundle bundle - = (ResourceBundle.LayoutFileBundle) mUnmarshaller.unmarshal(is); - resourceBundle.addLayoutBundle(bundle, fromSource); + final ResourceBundle.LayoutFileBundle bundle = ResourceBundle.LayoutFileBundle + .fromXML(is); + bundles.add(bundle); L.d("loaded layout info file %s", bundle); } finally { IOUtils.closeQuietly(is); } } + return bundles; } public void addEntry(String name, String contents) { diff --git a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressionsFromV1Compat.kt b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressionsFromV1Compat.kt new file mode 100644 index 00000000..4321b55a --- /dev/null +++ b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressionsFromV1Compat.kt @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2018 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.annotationprocessor + +import android.databinding.tool.CompilerChef +import android.databinding.tool.DataBindingCompilerArgs +import android.databinding.tool.store.ResourceBundle +import android.databinding.tool.writer.BindingMapperWriterV2 +import android.databinding.tool.writer.JavaFileWriter +import java.util.HashMap +import javax.annotation.processing.ProcessingEnvironment + +/** + * Loads intermediates from dependencies which are in V1, generates their code and creates a + * compiler chef specific to them. (to be able to treat them like a v2 dependency). + */ +class ProcessExpressionsFromV1Compat( + private val processingEnvironment: ProcessingEnvironment, + private val args : DataBindingCompilerArgs, + private val intermediates : List<ProcessExpressions.IntermediateV2>, + private val writer : JavaFileWriter) { + /** + * Returns a CompilerChef if we find V1 dependencies and generate code for them. + * Null if all dependencies are in v2 + */ + fun generate(): CompilerChef? { + // This is tricky: + // in libraries, we should generate them in a stripped way so that if same dependency + // shows up in 2 different path, it wont blow the final app + // if we are an app, we should generate it. + val isModuleInV2Lookup = HashMap<String, Boolean>() + + fun isModuleInV2(modulePackage : String) : Boolean { + return isModuleInV2Lookup.getOrPut(modulePackage) { + val mapperClass = BindingMapperWriterV2.createMapperQName(modulePackage) + // check if mapper exists for it + val typeElement = + processingEnvironment.elementUtils.getTypeElement(mapperClass) + typeElement != null + } + } + // mapping from key (layoutName) to generated code QName (or base class) + val classMapping = mutableMapOf<String, String>() + val compatBundle = ResourceBundle(args.modulePackage) + intermediates + .flatMap { + it.extractBundles() + } + .filterNot { layoutFileBundle -> + isModuleInV2(layoutFileBundle.modulePackage) + } + .forEach { + classMapping[it.fileName] = it.fullBindingClass + compatBundle.addLayoutBundle(it, false) + } + return writeResourceBundle( + resourceBundle = compatBundle, + compilerArgs = args.copyAsV1(COMPAT_PACKAGE) + ) + } + + /** + * Generates the code for v1 compat, returns the newly generated CompilerChef. + * Returns null if we don't generate anything. + */ + private fun writeResourceBundle( + resourceBundle: ResourceBundle, + compilerArgs: DataBindingCompilerArgs): CompilerChef? { + val compilerChef = CompilerChef.createChef( + resourceBundle, + writer, compilerArgs + ) + compilerChef.sealModels() + // write this only if we are compiling an app or a library test app. + // even if data binding is enabled for tests, we should not re-generate this. + if (compilerChef.hasAnythingToGenerate()) { + if (!compilerArgs.isEnableV2) { + compilerChef.writeViewBinderInterfaces(compilerArgs.isLibrary + && !compilerArgs.isTestVariant) + } + if (compilerArgs.isApp != compilerArgs.isTestVariant + || compilerArgs.isEnabledForTests && !compilerArgs.isLibrary + || compilerArgs.isEnableV2 + ) { + compilerChef.writeViewBinders(compilerArgs.minApi) + } + } else { + return null + } + return compilerChef + } + + companion object { + const val COMPAT_PACKAGE = "android.databinding.v1Compat" + } +} diff --git a/compiler/src/main/java/android/databinding/tool/CompilerChef.java b/compiler/src/main/java/android/databinding/tool/CompilerChef.java index b747d8b4..45638966 100644 --- a/compiler/src/main/java/android/databinding/tool/CompilerChef.java +++ b/compiler/src/main/java/android/databinding/tool/CompilerChef.java @@ -32,6 +32,8 @@ import android.databinding.tool.writer.BindingMapperWriterV2; import android.databinding.tool.writer.JavaFileWriter; import android.databinding.tool.writer.MergedBindingMapperWriter; import com.android.annotations.NonNull; +import com.android.annotations.Nullable; + import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.TypeSpec; @@ -43,6 +45,11 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.TypeElement; /** * Chef class for compiler. @@ -79,10 +86,17 @@ public class CompilerChef { private ResourceBundle mResourceBundle; private DataBinder mDataBinder; private boolean mEnableV2; + // the compiler chef we create for V1 dependencies + @Nullable + private CompilerChef mV1CompatChef; private CompilerChef() { } + public void setV1CompatChef(CompilerChef v1CompatChef) { + mV1CompatChef = v1CompatChef; + } + public static CompilerChef createChef( ResourceBundle bundle, JavaFileWriter fileWriter, @@ -171,6 +185,7 @@ public class CompilerChef { } public void writeDataBinderMapper( + ProcessingEnvironment processingEnv, DataBindingCompilerArgs compilerArgs, Map<String, Integer> brValueLookup, List<String> modulePackages) { @@ -186,6 +201,9 @@ public class CompilerChef { if (generateMapper) { writeMapperForModule(compilerArgs, brValueLookup); } + if (mV1CompatChef != null) { + writeMapperForV1Compat(compilerArgs, brValueLookup); + } // merged mapper is the one generated for the whole app that includes the mappers // generated for individual modules. @@ -198,7 +216,7 @@ public class CompilerChef { generateMergedMapper = false; } if (generateMergedMapper) { - writeMergedMapper(compilerArgs, modulePackages); + writeMergedMapper(processingEnv, compilerArgs, modulePackages); } } else { final String pkg = "android.databinding"; @@ -213,17 +231,52 @@ public class CompilerChef { } } + private void writeMapperForV1Compat( + DataBindingCompilerArgs compilerArgs, + Map<String, Integer> brValueLookup) { + BindingMapperWriter dbr = new BindingMapperWriter( + BindingMapperWriter.V1_COMPAT_MAPPER_PKG, + BindingMapperWriter.V1_COMPAT_MAPPER_NAME, + mV1CompatChef.getLayoutBinders(), + compilerArgs); + mFileWriter.writeToFile( + BindingMapperWriter.V1_COMPAT_MAPPER_PKG + "." + dbr.getClassName(), + dbr.write(brValueLookup)); + } + + public List<LayoutBinder> getLayoutBinders() { + return mDataBinder.getLayoutBinders(); + } + /** * Writes the mapper android.databinding.DataBinderMapperImpl which is a merged mapper * that includes all mappers from dependencies. */ private void writeMergedMapper( + ProcessingEnvironment processingEnv, DataBindingCompilerArgs compilerArgs, List<String> modulePackages) { + // figure out which mappers exists as they may not exist for v1 libs. + List<String> availableDependencyModules = modulePackages.stream() + .filter(modulePackage -> { + if (modulePackage.equals(compilerArgs.getModulePackage())) { + // mine will be generated + return true; + } + String mapper = BindingMapperWriterV2.createMapperQName(modulePackage); + TypeElement impl = processingEnv + .getElementUtils() + .getTypeElement(mapper); + return impl != null; + }).collect(Collectors.toList()); Set<String> featurePackageIds = loadFeaturePackageIds(compilerArgs); StringBuilder sb = new StringBuilder(); MergedBindingMapperWriter mergedBindingMapperWriter = - new MergedBindingMapperWriter(modulePackages, compilerArgs, featurePackageIds); + new MergedBindingMapperWriter( + availableDependencyModules, + compilerArgs, + featurePackageIds, + mV1CompatChef != null); TypeSpec mergedMapperSpec = mergedBindingMapperWriter.write(); try { JavaFile.builder(mergedBindingMapperWriter.getPkg(), mergedMapperSpec) diff --git a/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java b/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java index d1959bef..a87885bc 100644 --- a/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java +++ b/compiler/src/main/java/android/databinding/tool/util/GenerationalClassUtil.java @@ -69,13 +69,7 @@ public class GenerationalClassUtil { } private GenerationalClassUtil(DataBindingCompilerArgs args) { - if (args.isEnableV2()) { - mEnabledExtensions = new ExtensionFilter[]{ExtensionFilter.BR, - ExtensionFilter.SETTER_STORE}; - } else { - mEnabledExtensions = new ExtensionFilter[]{ExtensionFilter.BR, ExtensionFilter.LAYOUT, - ExtensionFilter.SETTER_STORE}; - } + mEnabledExtensions = ExtensionFilter.values(); if (StringUtils.isNotBlank(args.getAarOutFolder())) { mIncrementalOutDir = new File(args.getAarOutFolder(), DataBindingBuilder.INCREMENTAL_BIN_AAR_DIR); diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriter.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriter.kt index 66f39e4c..70921a52 100644 --- a/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriter.kt +++ b/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriter.kt @@ -167,4 +167,10 @@ class BindingMapperWriter(var pkg : String, var className: String, } } }.generate() + + companion object { + const val V1_COMPAT_MAPPER_NAME = "V1CompatDataBinderMapperImpl" + const val V1_COMPAT_MAPPER_PKG = "android.databinding" + const val V1_COMPAT_QNAME = V1_COMPAT_MAPPER_PKG + "." + V1_COMPAT_MAPPER_NAME + } } diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriterV2.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriterV2.kt index 40deff3a..baa30b6a 100644 --- a/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriterV2.kt +++ b/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriterV2.kt @@ -65,6 +65,9 @@ class BindingMapperWriterV2(private val genClassInfoLog: GenClassInfoLog, ClassName.get("android.util", "SparseIntArray") private val SPARSE_ARRAY = ClassName.get("android.util", "SparseArray") + + @JvmStatic + fun createMapperQName(modulePackage : String) = modulePackage + "." + IMPL_CLASS_NAME } private val rClassMap = mutableMapOf<String, ClassName>() diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/MergedBindingMapperWriter.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/MergedBindingMapperWriter.kt index f0d7e59b..8517c234 100644 --- a/compiler/src/main/kotlin/android/databinding/tool/writer/MergedBindingMapperWriter.kt +++ b/compiler/src/main/kotlin/android/databinding/tool/writer/MergedBindingMapperWriter.kt @@ -29,7 +29,8 @@ import javax.lang.model.element.Modifier class MergedBindingMapperWriter(private val packages: List<String>, compilerArgs: DataBindingCompilerArgs, - private val featurePackages : Set<String>) { + private val featurePackages : Set<String>, + private val hasV1CompatMapper: Boolean) { private val generateAsTest = compilerArgs.isTestVariant && compilerArgs.isApp private val generateTestOverride = !generateAsTest && compilerArgs.isEnabledForTests private val overrideField = FieldSpec.builder(BindingMapperWriterV2.DATA_BINDER_MAPPER, @@ -38,8 +39,8 @@ class MergedBindingMapperWriter(private val packages: List<String>, .build() companion object { - private val APP_CLASS_NAME = "DataBinderMapperImpl" - private val TEST_CLASS_NAME = "Test$APP_CLASS_NAME" + const val APP_CLASS_NAME = "DataBinderMapperImpl" + private const val TEST_CLASS_NAME = "Test$APP_CLASS_NAME" val MERGED_MAPPER_BASE: ClassName = ClassName.get( "android.databinding", "MergedDataBinderMapper") @@ -59,6 +60,12 @@ class MergedBindingMapperWriter(private val packages: List<String>, val mapper = ClassName.get(pkg, APP_CLASS_NAME) addStatement("addMapper(new $T())", mapper) } + if (hasV1CompatMapper) { + val compatMapper = ClassName.get( + BindingMapperWriter.V1_COMPAT_MAPPER_PKG, + BindingMapperWriter.V1_COMPAT_MAPPER_NAME) + addStatement("addMapper(new $T())", compatMapper) + } featurePackages.forEach { addStatement("addMapper($S)", it) } |