diff options
11 files changed, 184 insertions, 54 deletions
diff --git a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java index 44cdb4f1..3066bbdc 100644 --- a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java +++ b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessBindable.java @@ -22,10 +22,10 @@ import android.databinding.tool.CompilerChef.BindableHolder; import android.databinding.tool.util.GenerationalClassUtil; import android.databinding.tool.util.L; import android.databinding.tool.util.Preconditions; +import android.databinding.tool.writer.BRWriter; +import android.databinding.tool.writer.JavaFileWriter; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -117,34 +117,18 @@ public class ProcessBindable extends ProcessDataBinding.ProcessingStep implement for (Intermediate intermediate : previousIntermediates) { intermediate.captureProperties(properties); } - writeBRClass(useFinalFields, pkg, properties); + final JavaFileWriter writer = getWriter(); + BRWriter brWriter = new BRWriter(properties, useFinalFields); + writer.writeToFile(pkg + ".BR", brWriter.write(pkg)); + //writeBRClass(useFinalFields, pkg, properties); if (useFinalFields) { // generate BR for all previous packages for (Intermediate intermediate : previousIntermediates) { - writeBRClass(true, intermediate.getPackage(), - properties); + writer.writeToFile(intermediate.getPackage() + ".BR", + brWriter.write(intermediate.getPackage())); } } - } - - private void writeBRClass(boolean useFinalFields, String pkg, HashSet<String> properties) { - ArrayList<String> sortedProperties = new ArrayList<String>(); - sortedProperties.addAll(properties); - Collections.sort(sortedProperties); - StringBuilder out = new StringBuilder(); - String modifier = "public static " + (useFinalFields ? "final" : "") + " int "; - out.append("package " + pkg + ";\n\n" + - "public class BR {\n" + - " " + modifier + "_all = 0;\n" - ); - int id = 0; - for (String property : sortedProperties) { - id++; - out.append(" " + modifier + property + " = " + id + ";\n"); - } - out.append("}\n"); - - getWriter().writeToFile(pkg + ".BR", out.toString() ); + mCallback.onBrWriterReady(brWriter); } private String getPropertyName(Element element) { diff --git a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java index 944cc20a..bf88435c 100644 --- a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java +++ b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java @@ -16,9 +16,14 @@ package android.databinding.annotationprocessor; +import org.apache.commons.lang3.exception.ExceptionUtils; + import android.databinding.BindingBuildInfo; +import android.databinding.tool.CompilerChef; import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.util.Preconditions; import android.databinding.tool.writer.AnnotationJavaFileWriter; +import android.databinding.tool.writer.BRWriter; import android.databinding.tool.writer.JavaFileWriter; import java.util.Arrays; @@ -69,15 +74,46 @@ public class ProcessDataBinding extends AbstractProcessor { } private void initProcessingSteps() { - ProcessBindable processBindable = new ProcessBindable(); + final ProcessBindable processBindable = new ProcessBindable(); mProcessingSteps = Arrays.asList( new ProcessMethodAdapters(), - new ProcessExpressions(processBindable), + new ProcessExpressions(), processBindable ); + Callback dataBinderWriterCallback = new Callback() { + CompilerChef mChef; + BRWriter mBRWriter; + boolean mLibraryProject; + int mMinSdk; + + @Override + public void onChefReady(CompilerChef chef, boolean libraryProject, int minSdk) { + Preconditions.checkNull(mChef, "Cannot set compiler chef twice"); + chef.addBRVariables(processBindable); + mChef = chef; + mLibraryProject = libraryProject; + mMinSdk = minSdk; + considerWritingMapper(); + } + + private void considerWritingMapper() { + if (mLibraryProject || mChef == null || mBRWriter == null) { + return; + } + mChef.writeDataBinderMapper(mMinSdk, mBRWriter); + } + + @Override + public void onBrWriterReady(BRWriter brWriter) { + Preconditions.checkNull(mBRWriter, "Cannot set br writer twice"); + mBRWriter = brWriter; + considerWritingMapper(); + } + }; AnnotationJavaFileWriter javaFileWriter = new AnnotationJavaFileWriter(processingEnv); for (ProcessingStep step : mProcessingSteps) { step.mJavaFileWriter = javaFileWriter; + step.mCallback = dataBinderWriterCallback; } } @@ -93,6 +129,7 @@ public class ProcessDataBinding extends AbstractProcessor { public abstract static class ProcessingStep { private boolean mDone; private JavaFileWriter mJavaFileWriter; + protected Callback mCallback; protected JavaFileWriter getWriter() { return mJavaFileWriter; @@ -125,4 +162,9 @@ public class ProcessDataBinding extends AbstractProcessor { ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo); } + + interface Callback { + void onChefReady(CompilerChef chef, boolean libraryProject, int minSdk); + void onBrWriterReady(BRWriter brWriter); + } } diff --git a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java index a1866348..f948831a 100644 --- a/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java +++ b/compiler/src/main/java/android/databinding/annotationprocessor/ProcessExpressions.java @@ -26,6 +26,7 @@ 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.writer.BRWriter; import java.io.File; import java.io.FilenameFilter; @@ -47,13 +48,9 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep { private static final String LAYOUT_INFO_FILE_SUFFIX = "-layoutinfo.bin"; - private final ProcessBindable mProcessBindable; - - public ProcessExpressions(ProcessBindable processBindable) { - mProcessBindable = processBindable; + public ProcessExpressions() { } - @Override public boolean onHandleStep(RoundEnvironment roundEnvironment, ProcessingEnvironment processingEnvironment, BindingBuildInfo buildInfo) { @@ -123,11 +120,10 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep { } private void writeResourceBundle(ResourceBundle resourceBundle, boolean forLibraryModule, - int minSdk, String exportClassNamesTo) + final int minSdk, String exportClassNamesTo) throws JAXBException { - CompilerChef compilerChef = CompilerChef.createChef(resourceBundle, getWriter()); + final CompilerChef compilerChef = CompilerChef.createChef(resourceBundle, getWriter()); if (compilerChef.hasAnythingToGenerate()) { - compilerChef.addBRVariables(mProcessBindable); compilerChef.writeViewBinderInterfaces(forLibraryModule); if (!forLibraryModule) { compilerChef.writeViewBinders(minSdk); @@ -139,17 +135,15 @@ public class ProcessExpressions extends ProcessDataBinding.ProcessingStep { if (forLibraryModule) { Set<String> classNames = compilerChef.getWrittenClassNames(); String out = StringUtils.join(classNames, System.getProperty("line.separator")); - L.d("Writing list of classes to %s . \nList:%s", exportClassNamesTo, out); try { - FileUtils.write(new File(exportClassNamesTo), - out); + //noinspection ConstantConditions + FileUtils.write(new File(exportClassNamesTo), out); } catch (IOException e) { L.e(e, "Cannot create list of written classes"); } - } else { - compilerChef.writeDbrFile(minSdk); } + mCallback.onChefReady(compilerChef, forLibraryModule, minSdk); } public static interface Intermediate extends Serializable { diff --git a/compiler/src/main/java/android/databinding/tool/CompilerChef.java b/compiler/src/main/java/android/databinding/tool/CompilerChef.java index cb307e56..10cc3d43 100644 --- a/compiler/src/main/java/android/databinding/tool/CompilerChef.java +++ b/compiler/src/main/java/android/databinding/tool/CompilerChef.java @@ -15,6 +15,7 @@ package android.databinding.tool; import android.databinding.tool.store.ResourceBundle; import android.databinding.tool.util.L; +import android.databinding.tool.writer.BRWriter; import android.databinding.tool.writer.DataBinderWriter; import android.databinding.tool.writer.JavaFileWriter; @@ -59,12 +60,12 @@ public class CompilerChef { return mResourceBundle != null && mResourceBundle.getLayoutBundles().size() > 0; } - public void writeDbrFile(int minSdk) { + public void writeDataBinderMapper(int minSdk, BRWriter brWriter) { ensureDataBinder(); final String pkg = "android.databinding"; DataBinderWriter dbr = new DataBinderWriter(pkg, mResourceBundle.getAppPackage(), "DataBinderMapper", mDataBinder.getLayoutBinders(), minSdk); - mFileWriter.writeToFile(pkg + "." + dbr.getClassName(), dbr.write()); + mFileWriter.writeToFile(pkg + "." + dbr.getClassName(), dbr.write(brWriter)); } /** diff --git a/compiler/src/main/java/android/databinding/tool/LayoutBinder.java b/compiler/src/main/java/android/databinding/tool/LayoutBinder.java index 79d6bfc4..c9efe8f4 100644 --- a/compiler/src/main/java/android/databinding/tool/LayoutBinder.java +++ b/compiler/src/main/java/android/databinding/tool/LayoutBinder.java @@ -269,7 +269,6 @@ public class LayoutBinder implements ResolveListenersCallback { return mWriter.writeBaseClass(forLibrary); } - public String writeViewBinder(int minSdk) { mExprModel.seal(this); ensureWriter(); diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/BRWriter.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/BRWriter.kt new file mode 100644 index 00000000..9e41c2d6 --- /dev/null +++ b/compiler/src/main/kotlin/android/databinding/tool/writer/BRWriter.kt @@ -0,0 +1,35 @@ +/* + * 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.writer + +import kotlin.properties.Delegates + +class BRWriter(properties: Set<String>, val useFinal : Boolean) { + val indexedProps = properties.sort().withIndex() + public fun write(pkg : String): String = "package $pkg;${System.lineSeparator()}$klass" + val klass: String by Delegates.lazy { + kcode("") { + val prefix = if (useFinal) "final " else ""; + nl("public class BR {") { + tab("public static ${prefix}int _all = 0;") + indexedProps.forEach { + tab ("public static ${prefix}int ${it.value} = ${it.index + 1};") + } + } nl ("}") + }.generate() + } +}
\ No newline at end of file diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt index 34475ba8..29863395 100644 --- a/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt +++ b/compiler/src/main/kotlin/android/databinding/tool/writer/DataBinderWriter.kt @@ -17,19 +17,13 @@ import android.databinding.tool.LayoutBinder class DataBinderWriter(val pkg: String, val projectPackage: String, val className: String, val layoutBinders : List<LayoutBinder>, val minSdk : kotlin.Int) { - fun write() = kcode("") { + fun write(brWriter : BRWriter) = kcode("") { nl("package $pkg;") nl("import $projectPackage.BR;") nl("class $className {") { tab("final static int TARGET_MIN_SDK = ${minSdk};") nl("") - tab("private final java.util.HashMap<String, Integer> mLayoutIds;") - nl("") tab("public $className() {") { - tab("mLayoutIds = new java.util.HashMap<String, Integer>();") - layoutBinders.forEach { - tab("mLayoutIds.put(\"${it.getTag()}_0\", ${it.getModulePackage()}.R.layout.${it.getLayoutname()});") - } } tab("}") nl("") @@ -97,14 +91,45 @@ class DataBinderWriter(val pkg: String, val projectPackage: String, val classNam tab("}") tab("int getLayoutId(String tag) {") { - tab("Integer id = mLayoutIds.get(tag);") - tab("if (id == null) {") { - tab("return 0;") + tab("if (tag == null) {") { + tab("return 0;"); + } + tab("}") + // String.hashCode is well defined in the API so we can rely on it being the same on the device and the host machine + tab("final int code = tag.hashCode();"); + tab("switch(code) {") { + layoutBinders.groupBy {"${it.getTag()}_0".hashCode()}.forEach { + tab("case ${it.key}:") { + it.value.forEach { + tab("if(tag.equals(\"${it.getTag()}_0\"))") { + tab("return ${it.getModulePackage()}.R.layout.${it.getLayoutname()};") + } + } + tab("break;") + } + + } } tab("}") - tab("return id;") + tab("return 0;") } tab("}") + + tab("String convertBrIdToString(int id) {") { + tab("if (id < 0 || id >= InnerBrLookup.sKeys.length) {") { + tab("return null;") + } tab("}") + tab("return InnerBrLookup.sKeys[id];") + } tab("}") + + tab("private static class InnerBrLookup {") { + tab("static String[] sKeys = new String[]{") { + tab("\"_all\"") + brWriter.indexedProps.forEach { + tab(",\"${it.value}\"") + } + }.app("};") + } tab("}") } nl("}") }.generate() diff --git a/compilerCommon/src/main/java/android/databinding/tool/util/Preconditions.java b/compilerCommon/src/main/java/android/databinding/tool/util/Preconditions.java index f1a790cd..8041cd40 100644 --- a/compilerCommon/src/main/java/android/databinding/tool/util/Preconditions.java +++ b/compilerCommon/src/main/java/android/databinding/tool/util/Preconditions.java @@ -31,4 +31,10 @@ public class Preconditions { L.e(error, args); } } + + public static void checkNull(Object value, String error, Object... args) { + if (value != null) { + L.e(error, args); + } + } } diff --git a/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/DataBindingMapperTest.java b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/DataBindingMapperTest.java new file mode 100644 index 00000000..62904b49 --- /dev/null +++ b/integration-tests/TestApp/app/src/androidTestApi7/java/android/databinding/testapp/DataBindingMapperTest.java @@ -0,0 +1,30 @@ +/* + * 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.testapp; + +import android.databinding.DataBindingUtil; +import android.test.AndroidTestCase; +import android.databinding.testapp.BR; + +import java.lang.reflect.Field; + + +public class DataBindingMapperTest extends AndroidTestCase { + public void testBrIds() throws IllegalAccessException { + for (Field field : BR.class.getDeclaredFields()) { + assertEquals(field.getName(), + DataBindingUtil.convertBrIdToString((int) field.get(BR.class))); + } + } +} diff --git a/library/src/main/java/android/databinding/DataBinderMapper.java b/library/src/main/java/android/databinding/DataBinderMapper.java index be10d971..f20c480e 100644 --- a/library/src/main/java/android/databinding/DataBinderMapper.java +++ b/library/src/main/java/android/databinding/DataBinderMapper.java @@ -31,5 +31,8 @@ class DataBinderMapper { return null; } public int getLayoutId(String tag) { return 0; } + public String convertBrIdToString(int id) { + return null; + } public static int TARGET_MIN_SDK = 0; } diff --git a/library/src/main/java/android/databinding/DataBindingUtil.java b/library/src/main/java/android/databinding/DataBindingUtil.java index 85530c71..2208ab4d 100644 --- a/library/src/main/java/android/databinding/DataBindingUtil.java +++ b/library/src/main/java/android/databinding/DataBindingUtil.java @@ -186,4 +186,15 @@ public class DataBindingUtil { activity.setContentView(binding.getRoot(), binding.getRoot().getLayoutParams()); return binding; } + + /** + * Converts the given BR id to its string representation which might be useful for logging + * purposes. + * + * @param id The integer id, which should be a field from BR class. + * @return The name if the BR id or null if id is out of bounds. + */ + public static String convertBrIdToString(int id) { + return sMapper.convertBrIdToString(id); + } } |