From 9fdabe4b14ea69aa4c025f6cd95f28a05f3ce9be Mon Sep 17 00:00:00 2001 From: George Mount Date: Tue, 16 Dec 2014 15:25:56 -0800 Subject: [PATCH] Missed adding annotation processor code. --- .../main/java/android/binding/Bindable.java | 19 ++ .../annotationprocessor/ProcessBindable.java | 164 +++++++++++++++ .../javax.annotation.processing.Processor | 1 + .../renderer/binding_br_renderer.kt | 4 +- tools/data-binding/library/build.gradle | 1 + .../databinding/library/BindingContext.java | 190 ++++++++++++++++++ .../databinding/library/DataBinder.java | 19 +- .../databinding/library/DataBinderMapper.java | 2 +- .../databinding/library/ViewDataBinder.java | 154 -------------- .../samples/BindingDemo/build.gradle | 2 +- 10 files changed, 388 insertions(+), 168 deletions(-) create mode 100644 tools/data-binding/annotationprocessor/src/main/java/android/binding/Bindable.java create mode 100644 tools/data-binding/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessBindable.java create mode 100644 tools/data-binding/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor create mode 100644 tools/data-binding/library/src/main/java/com/android/databinding/library/BindingContext.java delete mode 100644 tools/data-binding/library/src/main/java/com/android/databinding/library/ViewDataBinder.java diff --git a/tools/data-binding/annotationprocessor/src/main/java/android/binding/Bindable.java b/tools/data-binding/annotationprocessor/src/main/java/android/binding/Bindable.java new file mode 100644 index 0000000000000..478a65832ccf0 --- /dev/null +++ b/tools/data-binding/annotationprocessor/src/main/java/android/binding/Bindable.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 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.binding; + +public @interface Bindable { +} diff --git a/tools/data-binding/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessBindable.java b/tools/data-binding/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessBindable.java new file mode 100644 index 0000000000000..6bd71e243da0d --- /dev/null +++ b/tools/data-binding/annotationprocessor/src/main/java/com/android/databinding/annotationprocessor/ProcessBindable.java @@ -0,0 +1,164 @@ +package com.android.databinding.annotationprocessor; + +import android.binding.Bindable; + +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeKind; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; + +@SupportedAnnotationTypes({"android.binding.Bindable"}) +@SupportedSourceVersion(SourceVersion.RELEASE_7) +public class ProcessBindable extends AbstractProcessor { + + private boolean mFileGenerated; + + public ProcessBindable() { + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (mFileGenerated) { + return false; + } + HashSet properties = new HashSet(); + for (Element element : roundEnv.getElementsAnnotatedWith(Bindable.class)) { +// processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, +// "Found Bindable: " + element); + String name = getPropertyName(element); + if (name != null) { + properties.add(name); + } + } + generateBR(properties); + mFileGenerated = true; + return true; + } + + private void generateBR(HashSet properties) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, + "************* Generating BR file from Bindable attributes"); + try { + ArrayList sortedProperties = new ArrayList(); + sortedProperties.addAll(properties); + Collections.sort(sortedProperties); + + JavaFileObject fileObject = processingEnv.getFiler() + .createSourceFile("android.binding.BR"); + Writer writer = fileObject.openWriter(); + writer.write("package android.binding;\n\n" + + "public final class BR {\n" + + " public static final int _all = 0;\n" + ); + int id = 0; + for (String property : sortedProperties) { + id++; + writer.write(" public static final int " + property + " = " + id + ";\n"); + } + writer.write("}\n"); + writer.close(); + } catch (IOException e) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + "Could not generate BR file " + e.getLocalizedMessage()); + } + } + + private String getPropertyName(Element element) { + switch (element.getKind()) { + case FIELD: + return stripPrefixFromField((VariableElement) element); + case METHOD: + return stripPrefixFromMethod((ExecutableElement) element); + default: + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + "@Bindable is not allowed on " + element.getKind(), element); + return null; + } + } + + private static String stripPrefixFromField(VariableElement element) { + Name name = element.getSimpleName(); + if (name.length() >= 2) { + char firstChar = name.charAt(0); + char secondChar = name.charAt(1); + if ((firstChar == 'm' && Character.isUpperCase(secondChar)) || + (firstChar == '_' && Character.isJavaIdentifierStart(secondChar))) { + return "" + Character.toLowerCase(secondChar) + name.subSequence(2, name.length()); + } + } + return name.toString(); + } + + private String stripPrefixFromMethod(ExecutableElement element) { + Name name = element.getSimpleName(); + CharSequence propertyName; + if (isGetter(element) || isSetter(element)) { + propertyName = name.subSequence(3, name.length()); + } else if (isBooleanGetter(element)) { + propertyName = name.subSequence(2, name.length()); + } else { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + "@Bindable associated with method must follow JavaBeans convention", element); + return null; + } + char firstChar = propertyName.charAt(0); + return "" + Character.toLowerCase(firstChar) + + propertyName.subSequence(1, propertyName.length()); + } + + private static boolean prefixes(CharSequence sequence, String prefix) { + boolean prefixes = false; + if (sequence.length() > prefix.length()) { + int count = prefix.length(); + prefixes = true; + for (int i = 0; i < count; i++) { + if (sequence.charAt(i) != prefix.charAt(i)) { + prefixes = false; + break; + } + } + } + return prefixes; + } + + private static boolean isGetter(ExecutableElement element) { + Name name = element.getSimpleName(); + return prefixes(name, "get") && + Character.isJavaIdentifierStart(name.charAt(3)) && + element.getParameters().isEmpty() && + element.getReturnType().getKind() != TypeKind.VOID; + } + + private static boolean isSetter(ExecutableElement element) { + Name name = element.getSimpleName(); + return prefixes(name, "set") && + Character.isJavaIdentifierStart(name.charAt(3)) && + element.getParameters().size() == 1 && + element.getReturnType().getKind() == TypeKind.VOID; + } + + private static boolean isBooleanGetter(ExecutableElement element) { + Name name = element.getSimpleName(); + return prefixes(name, "is") && + Character.isJavaIdentifierStart(name.charAt(2)) && + element.getParameters().isEmpty() && + element.getReturnType().getKind() == TypeKind.BOOLEAN; + } +} diff --git a/tools/data-binding/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/tools/data-binding/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000000000..2128365b26601 --- /dev/null +++ b/tools/data-binding/annotationprocessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +com.android.databinding.annotationprocessor.ProcessBindable diff --git a/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/renderer/binding_br_renderer.kt b/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/renderer/binding_br_renderer.kt index 3c743aa35cdb0..81bf223667dc7 100644 --- a/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/renderer/binding_br_renderer.kt +++ b/tools/data-binding/compiler/src/main/kotlin/com/android/databinding/renderer/binding_br_renderer.kt @@ -36,8 +36,8 @@ class BrRenderer(val pkg : String, val className : String, val vbrs : List def name = variant.buildType.name diff --git a/tools/data-binding/library/src/main/java/com/android/databinding/library/BindingContext.java b/tools/data-binding/library/src/main/java/com/android/databinding/library/BindingContext.java new file mode 100644 index 0000000000000..397ca95e82070 --- /dev/null +++ b/tools/data-binding/library/src/main/java/com/android/databinding/library/BindingContext.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2014 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 com.android.databinding.library; + +import android.annotation.TargetApi; +import android.os.Build; +import android.view.View; + +import java.lang.Override; +import java.lang.Runnable; +import java.lang.ref.WeakReference; + +/** + * BindingContext is the base class used for data binding to an XML layout. Each + * layout will generate a subclass of BindingContext in the form LayoutNameBindingContext, + * where LayoutName is layout/layout_name.xml. All layout files with the same name + * will share the same subclass of BindingContext. For example, if your application + * contains: + *
+ * layout/my_layout.xml
+ * layout-land/my_layout.xml
+ * layout-port/my_layout.xml
+ * 
+ * a class with the name MyLayoutBindingContext will be generated that handles binding + * for all files. + * + *

Use the variable setters and getters in the BindingContext subclass to assign values to + * the bound Views.

+ */ +public abstract class BindingContext { + + /** + * The root View as returned from the layout inflation. + */ + public final View root; + + private final WeakReferencedListener[] mChangeListeners; + private boolean mPendingRebind = false; + + private Runnable mRebindRunnable = new Runnable() { + @Override + public void run() { + rebindDirty(); + mPendingRebind = false; + } + }; + + public BindingContext(View root, int localFieldCount) { + mChangeListeners = new WeakReferencedListener[localFieldCount]; + this.root = root; + + if (root != null) { + // TODO: set a resource tag in 11+ + root.setTag(this); + } + } + + protected abstract boolean onFieldChange(int mLocalFieldId, Object object, int fieldId); + protected abstract void rebindDirty(); + + private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) { + boolean result = onFieldChange(mLocalFieldId, object, fieldId); + if (result) { + requestRebind(); + } + } + + protected final boolean unregisterFrom(int localFieldId) { + WeakReferencedListener listener = mChangeListeners[localFieldId]; + if (listener != null) { + return listener.unregister(); + } + return false; + } + + protected void requestRebind() { + if (mPendingRebind) { + return; + } + mPendingRebind = true; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) { + postJellyBean(); + } else { + postIceCreamSandwich(); + } + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + private void postJellyBean() { + root.postOnAnimation(mRebindRunnable); + } + + private void postIceCreamSandwich() { + root.post(mRebindRunnable); + } + + protected Object getObservedField(int localFieldId) { + WeakReferencedListener listener = mChangeListeners[localFieldId]; + if (listener == null) { + return null; + } + return listener.getTarget(); + } + + protected boolean updateRegistration(int localFieldId, Observable observable) { + WeakReferencedListener listener = mChangeListeners[localFieldId]; + if (listener == null) { + if (observable == null) { + return false; + } else { + listener = new WeakReferencedListener(this, localFieldId); + mChangeListeners[localFieldId] = listener; + } + } + return listener.register(observable); + } + + protected void registerTo(int localFieldId, Observable observable) { + if (observable == null) { + return; + } + WeakReferencedListener listener = mChangeListeners[localFieldId]; + if (listener == null) { + listener = new WeakReferencedListener(this, localFieldId); + mChangeListeners[localFieldId] = listener; + } + listener.register(observable); + } + + private static class WeakReferencedListener implements OnPropertyChangedListener { + private final WeakReference mBindingContext; + private final int mFieldId; + private Observable mTarget; + + public WeakReferencedListener(BindingContext bindingContext, int fieldId) { + mBindingContext = new WeakReference<>(bindingContext); + mFieldId = fieldId; + } + + @Override + public void onPropertyChanged(int propertyId) { + BindingContext bindingContext = mBindingContext.get(); + if (bindingContext != null) { + bindingContext.handleFieldChange(mFieldId, mTarget, propertyId); + } + } + + public boolean unregister() { + if (mTarget != null) { + mTarget.removeListener(this); + mTarget = null; + return true; + } else { + return false; + } + } + + public boolean register(Observable target) { + if (mTarget == target) { + return false; + } else if (mTarget != null) { + mTarget.removeListener(this); + } + mTarget = target; + if (target != null) { + target.addListener(this); + } + return true; + } + + public Observable getTarget() { + return mTarget; + } + } + +} diff --git a/tools/data-binding/library/src/main/java/com/android/databinding/library/DataBinder.java b/tools/data-binding/library/src/main/java/com/android/databinding/library/DataBinder.java index 8f42f806af6e6..65f497e953715 100644 --- a/tools/data-binding/library/src/main/java/com/android/databinding/library/DataBinder.java +++ b/tools/data-binding/library/src/main/java/com/android/databinding/library/DataBinder.java @@ -17,7 +17,6 @@ package com.android.databinding.library; import android.content.Context; -import android.util.Log; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; @@ -30,9 +29,9 @@ public class DataBinder { static DataBinderMapper sMapper; - private WeakHashMap mDataBinderMap = new WeakHashMap<>(); + private WeakHashMap mDataBinderMap = new WeakHashMap<>(); - private SparseArray> mDataBinderById = new SparseArray<>(); + private SparseArray> mDataBinderById = new SparseArray<>(); private static DataBinderMapper getMapper() { if (sMapper != null) { @@ -55,7 +54,7 @@ public class DataBinder { public View inflate(Context context, int layoutId) { View view = LayoutInflater.from(context).inflate(layoutId, null); - ViewDataBinder dataBinder = getMapper().getDataBinder(view, layoutId); + BindingContext dataBinder = getMapper().getDataBinder(view, layoutId); if (dataBinder != null) { mDataBinderMap.put(view, dataBinder); mDataBinderById.put(layoutId, new WeakReference<>(dataBinder)); @@ -64,7 +63,7 @@ public class DataBinder { } public boolean setVariable(View view, String name, Object variable) { - ViewDataBinder binder = getDataBinder(view); + BindingContext binder = getDataBinder(view); if (binder == null) { return false; } @@ -76,7 +75,7 @@ public class DataBinder { } public boolean setVariable(int layoutId, String name, Object variable) { - ViewDataBinder binder = getDataBinder(layoutId); + BindingContext binder = getDataBinder(layoutId); if (binder == null) { return false; } @@ -87,7 +86,7 @@ public class DataBinder { return binder.setVariable(nameId, variable); } - public ViewDataBinder getDataBinder(View view) { + public BindingContext getDataBinder(View view) { return mDataBinderMap.get(view); } @@ -99,7 +98,7 @@ public class DataBinder { return (T) getDataBinder(layoutId); } - public static ViewDataBinder createBinder(Context context, int layoutId, ViewGroup parent) { + public static BindingContext createBinder(Context context, int layoutId, ViewGroup parent) { return getMapper() .getDataBinder(LayoutInflater.from(context).inflate(layoutId, parent, false), layoutId); @@ -109,8 +108,8 @@ public class DataBinder { return (T) createBinder(context, layoutId, parent); } - public ViewDataBinder getDataBinder(int layoutId) { - WeakReference weakReference = mDataBinderById.get(layoutId); + public BindingContext getDataBinder(int layoutId) { + WeakReference weakReference = mDataBinderById.get(layoutId); if (weakReference == null) { return null; } diff --git a/tools/data-binding/library/src/main/java/com/android/databinding/library/DataBinderMapper.java b/tools/data-binding/library/src/main/java/com/android/databinding/library/DataBinderMapper.java index 0312e026cb90c..8658a014825d1 100644 --- a/tools/data-binding/library/src/main/java/com/android/databinding/library/DataBinderMapper.java +++ b/tools/data-binding/library/src/main/java/com/android/databinding/library/DataBinderMapper.java @@ -19,6 +19,6 @@ package com.android.databinding.library; import android.view.View; public interface DataBinderMapper { - ViewDataBinder getDataBinder(View view, int layoutId); + BindingContext getDataBinder(View view, int layoutId); public int getId(String key); } diff --git a/tools/data-binding/library/src/main/java/com/android/databinding/library/ViewDataBinder.java b/tools/data-binding/library/src/main/java/com/android/databinding/library/ViewDataBinder.java deleted file mode 100644 index c0ac85cb0c6e1..0000000000000 --- a/tools/data-binding/library/src/main/java/com/android/databinding/library/ViewDataBinder.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2014 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 com.android.databinding.library; - -import android.view.View; - -import java.lang.Override; -import java.lang.Runnable; -import java.lang.ref.WeakReference; - -abstract public class ViewDataBinder { - WeakReferencedListener[] mLocalFieldObservers; - protected abstract boolean onFieldChange(int mLocalFieldId, Object object, int fieldId); - public abstract boolean setVariable(int variableId, Object variable); - public abstract void rebindDirty(); - private final View mRoot; - - private boolean mPendingRebind = false; - private Runnable mRebindRunnable = new Runnable() { - @Override - public void run() { - rebindDirty(); - mPendingRebind = false; - } - }; - - public ViewDataBinder(View root, int localFieldCount) { - mLocalFieldObservers = new WeakReferencedListener[localFieldCount]; - mRoot = root; - } - - public View getRoot() { - return mRoot; - } - - private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) { - boolean result = onFieldChange(mLocalFieldId, object, fieldId); - if (result) { - requestRebind(); - } - } - - protected boolean unregisterFrom(int localFieldId) { - WeakReferencedListener listener = mLocalFieldObservers[localFieldId]; - if (listener != null) { - return listener.unregister(); - } - return false; - } - - protected void requestRebind() { - if (mPendingRebind) { - return; - } - mPendingRebind = true; - mRoot.postOnAnimation(mRebindRunnable); - } - - protected Object getObservedField(int localFieldId) { - WeakReferencedListener listener = mLocalFieldObservers[localFieldId]; - if (listener == null) { - return null; - } - return listener.getTarget(); - } - - protected boolean updateRegistration(int localFieldId, Observable observable) { - if (observable == null) { - return unregisterFrom(localFieldId); - } - WeakReferencedListener listener = mLocalFieldObservers[localFieldId]; - if (listener == null) { - registerTo(localFieldId, observable); - return true; - } - if (listener.getTarget() == observable) { - return false;//nothing to do, same object - } - unregisterFrom(localFieldId); - registerTo(localFieldId, observable); - return true; - } - - protected void registerTo(int localFieldId, Observable observable) { - if (observable == null) { - return; - } - WeakReferencedListener listener = mLocalFieldObservers[localFieldId]; - if (listener == null) { - listener = new ObservableFieldListener(localFieldId); - mLocalFieldObservers[localFieldId] = listener; - } - listener.setTarget(observable); - } - - protected abstract class WeakReferencedListener implements OnPropertyChangedListener { - WeakReference mTarget; - - public WeakReferencedListener() { - } - - public void setTarget(Observable observable) { - if (observable != null) { - mTarget = new WeakReference<>(observable); - observable.addListener(this); - } else { - mTarget = null; - } - } - - public boolean unregister() { - Observable oldTarget = getTarget(); - if (oldTarget != null) { - oldTarget.removeListener(this); - } - mTarget = null; - return oldTarget != null; - } - - Observable getTarget() { - return mTarget == null ? null : mTarget.get(); - } - } - - protected class ObservableFieldListener extends WeakReferencedListener { - final int mLocalFieldId; - public ObservableFieldListener(int localFieldId) { - mLocalFieldId = localFieldId; - } - - @Override - public void onPropertyChanged(int fieldId) { - Observable obj = getTarget(); - if (obj == null) { - return;//how come i live if it died ? - } - ViewDataBinder.this.handleFieldChange(mLocalFieldId, obj, fieldId); - } - } -} diff --git a/tools/data-binding/samples/BindingDemo/build.gradle b/tools/data-binding/samples/BindingDemo/build.gradle index 6dad139f0c9d7..e4abd4e2cab6f 100644 --- a/tools/data-binding/samples/BindingDemo/build.gradle +++ b/tools/data-binding/samples/BindingDemo/build.gradle @@ -23,7 +23,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:0.14.2' + classpath 'com.android.tools.build:gradle:1.0.0' classpath 'com.android.databinding:dataBinder:0.3-SNAPSHOT' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files