summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
authorYigit Boyar <yboyar@google.com>2015-11-10 18:16:36 -0800
committerYigit Boyar <yboyar@google.com>2015-11-11 13:18:22 -0800
commit28e7064d455e2ef9da31c817dfc05ec7405c60df (patch)
tree82dcf9066526452f185cbddcc189b3c43c6a235c /compiler
parentfccc888b6465488b9ad74f4ca0b58c85502ae089 (diff)
downloaddata-binding-28e7064d455e2ef9da31c817dfc05ec7405c60df.tar.gz
Override layout file metadata from module
If a layout file is inherited from a module but also exists in the app, w~e force the app version to the metadata of the module version. This means forcing its created class location to be the same as the module. This usually happens when gradle or aapt generates some layout during app compilation. Bug: 25369165 Change-Id: I5d2002ac04d16cfe9935fe5580548344b19b4aca
Diffstat (limited to 'compiler')
-rw-r--r--compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java12
-rw-r--r--compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java131
2 files changed, 105 insertions, 38 deletions
diff --git a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java
index d63ec410..4d1fadb3 100644
--- a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java
+++ b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java
@@ -20,6 +20,7 @@ import android.databinding.BindingBuildInfo;
import android.databinding.tool.CompilerChef;
import android.databinding.tool.processing.Scope;
import android.databinding.tool.reflection.ModelAnalyzer;
+import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
import android.databinding.tool.writer.AnnotationJavaFileWriter;
import android.databinding.tool.writer.BRWriter;
@@ -35,6 +36,7 @@ import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
+import javax.xml.bind.JAXBException;
@SupportedAnnotationTypes({
"android.databinding.BindingAdapter",
@@ -60,7 +62,11 @@ public class ProcessDataBinding extends AbstractProcessor {
}
boolean done = true;
for (ProcessingStep step : mProcessingSteps) {
- done = step.runStep(roundEnv, processingEnv, buildInfo) && done;
+ try {
+ done = step.runStep(roundEnv, processingEnv, buildInfo) && done;
+ } catch (JAXBException e) {
+ L.e(e, "Exception while handling step %s", step);
+ }
}
if (roundEnv.processingOver()) {
for (ProcessingStep step : mProcessingSteps) {
@@ -141,7 +147,7 @@ public class ProcessDataBinding extends AbstractProcessor {
private boolean runStep(RoundEnvironment roundEnvironment,
ProcessingEnvironment processingEnvironment,
- BindingBuildInfo buildInfo) {
+ BindingBuildInfo buildInfo) throws JAXBException {
if (mDone) {
return true;
}
@@ -156,7 +162,7 @@ public class ProcessDataBinding extends AbstractProcessor {
*/
abstract public boolean onHandleStep(RoundEnvironment roundEnvironment,
ProcessingEnvironment processingEnvironment,
- BindingBuildInfo buildInfo);
+ BindingBuildInfo buildInfo) throws JAXBException;
/**
* Invoked when processing is done. A good place to generate the output if the
diff --git a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java
index 26706a6a..0fca9c0f 100644
--- a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java
+++ b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java
@@ -23,17 +23,21 @@ import org.apache.commons.lang3.SystemUtils;
import android.databinding.BindingBuildInfo;
import android.databinding.tool.CompilerChef;
+import android.databinding.tool.LayoutXmlProcessor;
import android.databinding.tool.reflection.SdkUtil;
import android.databinding.tool.store.ResourceBundle;
import android.databinding.tool.util.GenerationalClassUtil;
import android.databinding.tool.util.L;
+import android.databinding.tool.util.Preconditions;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -50,30 +54,55 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep {
@Override
public boolean onHandleStep(RoundEnvironment roundEnvironment,
- ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
+ ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo)
+ throws JAXBException {
ResourceBundle resourceBundle;
SdkUtil.initialize(buildInfo.minSdk(), new File(buildInfo.sdkRoot()));
resourceBundle = new ResourceBundle(buildInfo.modulePackage());
- List<Intermediate> intermediateList =
- GenerationalClassUtil.loadObjects(
- GenerationalClassUtil.ExtensionFilter.LAYOUT);
- IntermediateV1 mine = createIntermediateFromLayouts(buildInfo.layoutInfoDir());
+ List<IntermediateV2> intermediateList = loadDependencyIntermediates();
+ for (Intermediate intermediate : intermediateList) {
+ try {
+ intermediate.appendTo(resourceBundle);
+ } catch (Throwable throwable) {
+ L.e(throwable, "unable to prepare resource bundle");
+ }
+ }
+
+ IntermediateV2 mine = createIntermediateFromLayouts(buildInfo.layoutInfoDir(),
+ intermediateList);
if (mine != null) {
- mine.removeOverridden(intermediateList);
+ mine.updateOverridden(resourceBundle);
intermediateList.add(mine);
saveIntermediate(processingEnvironment, buildInfo, mine);
+ mine.appendTo(resourceBundle);
}
// generate them here so that bindable parser can read
try {
- generateBinders(resourceBundle, buildInfo, intermediateList);
+ writeResourceBundle(resourceBundle, buildInfo.isLibrary(), buildInfo.minSdk(),
+ buildInfo.exportClassListTo());
} catch (Throwable t) {
L.e(t, "cannot generate view binders");
}
return true;
}
+ private List<IntermediateV2> loadDependencyIntermediates() {
+ final List<IntermediateV2> original = GenerationalClassUtil.loadObjects(
+ GenerationalClassUtil.ExtensionFilter.LAYOUT);
+ final List<IntermediateV2> upgraded = new ArrayList<IntermediateV2>(original.size());
+ for (Intermediate intermediate : original) {
+ final Intermediate updatedIntermediate = intermediate.upgrade();
+ Preconditions.check(updatedIntermediate instanceof IntermediateV2, "Incompatible data"
+ + " binding dependency. Please update your dependencies or recompile them with"
+ + " application module's data binding version.");
+ //noinspection ConstantConditions
+ upgraded.add((IntermediateV2) updatedIntermediate);
+ }
+ return upgraded;
+ }
+
private void saveIntermediate(ProcessingEnvironment processingEnvironment,
- BindingBuildInfo buildInfo, IntermediateV1 intermediate) {
+ BindingBuildInfo buildInfo, IntermediateV2 intermediate) {
GenerationalClassUtil.writeIntermediateFile(processingEnvironment,
buildInfo.modulePackage(), buildInfo.modulePackage() +
GenerationalClassUtil.ExtensionFilter.LAYOUT.getExtension(),
@@ -85,27 +114,22 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep {
ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) {
}
- private void generateBinders(ResourceBundle resourceBundle, BindingBuildInfo buildInfo,
- List<Intermediate> intermediates)
- throws Throwable {
- for (Intermediate intermediate : intermediates) {
- intermediate.appendTo(resourceBundle);
+ private IntermediateV2 createIntermediateFromLayouts(String layoutInfoFolderPath,
+ List<IntermediateV2> intermediateList) {
+ final Set<String> excludeList = new HashSet<String>();
+ for (IntermediateV2 lib : intermediateList) {
+ excludeList.addAll(lib.mLayoutInfoMap.keySet());
}
- writeResourceBundle(resourceBundle, buildInfo.isLibrary(), buildInfo.minSdk(),
- buildInfo.exportClassListTo());
- }
-
- private IntermediateV1 createIntermediateFromLayouts(String layoutInfoFolderPath) {
final File layoutInfoFolder = new File(layoutInfoFolderPath);
if (!layoutInfoFolder.isDirectory()) {
L.d("layout info folder does not exist, skipping for %s", layoutInfoFolderPath);
return null;
}
- IntermediateV1 result = new IntermediateV1();
+ IntermediateV2 result = new IntermediateV2();
for (File layoutFile : layoutInfoFolder.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
- return name.endsWith(".xml");
+ return name.endsWith(".xml") && !excludeList.contains(name);
}
})) {
try {
@@ -146,11 +170,11 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep {
mCallback.onChefReady(compilerChef, forLibraryModule, minSdk);
}
- public static interface Intermediate extends Serializable {
+ public interface Intermediate extends Serializable {
Intermediate upgrade();
- public void appendTo(ResourceBundle resourceBundle) throws Throwable;
+ void appendTo(ResourceBundle resourceBundle) throws Throwable;
}
public static class IntermediateV1 implements Intermediate {
@@ -162,7 +186,10 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep {
@Override
public Intermediate upgrade() {
- return this;
+ final IntermediateV2 updated = new IntermediateV2();
+ updated.mLayoutInfoMap = mLayoutInfoMap;
+ updated.mUnmarshaller = mUnmarshaller;
+ return updated;
}
@Override
@@ -189,19 +216,53 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep {
mLayoutInfoMap.put(name, contents);
}
+ // keeping the method to match deserialized structure
+ @SuppressWarnings("unused")
public void removeOverridden(List<Intermediate> existing) {
- // this is the way we get rid of files that are copied from previous modules
- // it is important to do this before saving the intermediate file
- for (Intermediate old : existing) {
- if (old instanceof IntermediateV1) {
- IntermediateV1 other = (IntermediateV1) old;
- for (String key : other.mLayoutInfoMap.keySet()) {
- // TODO we should consider the original file as the key here
- // but aapt probably cannot provide that information
- if (mLayoutInfoMap.remove(key) != null) {
- L.d("removing %s from bundle because it came from another module", key);
- }
- }
+ }
+ }
+
+ public static class IntermediateV2 extends IntermediateV1 {
+ // specify so that we can define updates ourselves.
+ private static final long serialVersionUID = 2L;
+ @Override
+ public void appendTo(ResourceBundle resourceBundle) throws JAXBException {
+ for (Map.Entry<String, String> entry : mLayoutInfoMap.entrySet()) {
+ final InputStream is = IOUtils.toInputStream(entry.getValue());
+ try {
+ final ResourceBundle.LayoutFileBundle bundle = ResourceBundle.LayoutFileBundle
+ .fromXML(is);
+ resourceBundle.addLayoutBundle(bundle);
+ L.d("loaded layout info file %s", bundle);
+ } finally {
+ IOUtils.closeQuietly(is);
+ }
+ }
+ }
+
+ /**
+ * if a layout is overridden from a module (which happens when layout is auto-generated),
+ * we need to update its contents from the class that overrides it.
+ * This must be done before this bundle is saved, otherwise, it will not be recognized
+ * when it is used in another project.
+ */
+ public void updateOverridden(ResourceBundle bundle) throws JAXBException {
+ // When a layout is copied from inherited module, it is eleminated while reading
+ // info files. (createIntermediateFromLayouts).
+ // Build process may also duplicate some files at compile time. This is where
+ // we detect those copies and force inherit their module and classname information.
+ final HashMap<String, List<ResourceBundle.LayoutFileBundle>> bundles = bundle
+ .getLayoutBundles();
+ for (Map.Entry<String, String> info : mLayoutInfoMap.entrySet()) {
+ String key = LayoutXmlProcessor.exportLayoutNameFromInfoFileName(info.getKey());
+ final List<ResourceBundle.LayoutFileBundle> existingList = bundles.get(key);
+ if (existingList != null && !existingList.isEmpty()) {
+ ResourceBundle.LayoutFileBundle myBundle = ResourceBundle.LayoutFileBundle
+ .fromXML(IOUtils.toInputStream(info.getValue()));
+ final ResourceBundle.LayoutFileBundle inheritFrom = existingList.get(0);
+ myBundle.inheritConfigurationFrom(inheritFrom);
+ L.d("inheriting data for %s (%s) from %s", info.getKey(), key, inheritFrom);
+ mLayoutInfoMap.put(info.getKey(), myBundle.toXML());
}
}
}