Missed adding annotation processor code.

This commit is contained in:
George Mount
2014-12-16 15:25:56 -08:00
parent 4e84d3dea3
commit 9fdabe4b14
10 changed files with 388 additions and 168 deletions

View File

@@ -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 {
}

View File

@@ -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<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (mFileGenerated) {
return false;
}
HashSet<String> properties = new HashSet<String>();
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<String> properties) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
"************* Generating BR file from Bindable attributes");
try {
ArrayList<String> sortedProperties = new ArrayList<String>();
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;
}
}

View File

@@ -0,0 +1 @@
com.android.databinding.annotationprocessor.ProcessBindable

View File

@@ -36,8 +36,8 @@ class BrRenderer(val pkg : String, val className : String, val vbrs : List<ViewE
}
}
public fun toInt(key : String) : Int = if (key == "") keyToInt.get("__") else keyToInt.get(key) ?: -1
public fun toIntS(key : String) : String = "${className}.${if (key == "") "__" else key}"
//public fun toInt(key : String) : Int = if (key == "") keyToInt.get("__") else keyToInt.get(key) ?: -1
public fun toIntS(key : String) : String = "${if (key == "") "android.binding.BR._all" else "android.binding.BR.$key"}"
public fun render() : String {
return """
package $pkg;

View File

@@ -57,6 +57,7 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
//create jar tasks
android.libraryVariants.all { variant ->
def name = variant.buildType.name

View File

@@ -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:
* <pre>
* layout/my_layout.xml
* layout-land/my_layout.xml
* layout-port/my_layout.xml
* </pre>
* a class with the name MyLayoutBindingContext will be generated that handles binding
* for all files.
*
* <p>Use the variable setters and getters in the BindingContext subclass to assign values to
* the bound Views.</p>
*/
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<BindingContext> 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;
}
}
}

View File

@@ -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<View, ViewDataBinder> mDataBinderMap = new WeakHashMap<>();
private WeakHashMap<View, BindingContext> mDataBinderMap = new WeakHashMap<>();
private SparseArray<WeakReference<ViewDataBinder>> mDataBinderById = new SparseArray<>();
private SparseArray<WeakReference<BindingContext>> 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<ViewDataBinder> weakReference = mDataBinderById.get(layoutId);
public BindingContext getDataBinder(int layoutId) {
WeakReference<BindingContext> weakReference = mDataBinderById.get(layoutId);
if (weakReference == null) {
return null;
}

View File

@@ -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);
}

View File

@@ -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<Observable> 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);
}
}
}

View File

@@ -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