From 11df39c91611b9ff2d7c87a9a9829251a015bccf Mon Sep 17 00:00:00 2001 From: George Mount Date: Tue, 15 Mar 2016 16:35:50 -0700 Subject: Added simple inverted String conversion. When binding a primitive to an EditText, a common pattern is to use '@{"" + value}'. This, however, doesn't allow for a two-way data binding expressions. To mitigate the need for conversion functions, a simple inversion for this expression wsa implemented that just converts value from a String when possible. This CL also fixes a bug in which a method matching the first parameter was always chosen, reguardless of the second and further parameters. Change-Id: I36828d9f54d2073965358fceb140b2d5e6328919 --- .../tool/reflection/InjectedBindingClass.java | 217 -------------------- .../tool/reflection/InjectedBindingClassField.java | 65 ------ .../reflection/InjectedBindingClassMethod.java | 111 ----------- .../databinding/tool/reflection/InjectedClass.java | 218 +++++++++++++++++++++ .../databinding/tool/reflection/InjectedField.java | 65 ++++++ .../tool/reflection/InjectedMethod.java | 146 ++++++++++++++ .../databinding/tool/reflection/ModelAnalyzer.java | 32 ++- .../databinding/tool/reflection/ModelMethod.java | 3 + 8 files changed, 460 insertions(+), 397 deletions(-) delete mode 100644 compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClass.java delete mode 100644 compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassField.java delete mode 100644 compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassMethod.java create mode 100644 compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java create mode 100644 compiler/src/main/java/android/databinding/tool/reflection/InjectedField.java create mode 100644 compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java (limited to 'compiler/src/main/java/android/databinding/tool/reflection') diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClass.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClass.java deleted file mode 100644 index 4759a9a6..00000000 --- a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClass.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) 2016 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.reflection; - -import android.databinding.tool.util.StringUtils; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -/** - * A class that can be used by ModelAnalyzer without any backing model. This is used - * for ViewDataBinding subclasses that haven't been generated yet, but we still want - * to resolve methods and fields for them. - * - * @see ModelAnalyzer#injectViewDataBinding(String, Map, Map) - */ -public class InjectedBindingClass extends ModelClass { - private final String mClassName; - private final String mSuperClass; - private final Map mVariables; - private final Map mFields; - - public InjectedBindingClass(String className, String superClass, Map variables, - Map fields) { - mClassName = className; - mSuperClass = superClass; - mVariables = variables; - mFields = fields; - } - - @Override - public String toJavaCode() { - return mClassName; - } - - @Override - public boolean isArray() { - return false; - } - - @Override - public ModelClass getComponentType() { - return null; - } - - @Override - public boolean isNullable() { - return true; - } - - @Override - public boolean isPrimitive() { - return false; - } - - @Override - public boolean isBoolean() { - return false; - } - - @Override - public boolean isChar() { - return false; - } - - @Override - public boolean isByte() { - return false; - } - - @Override - public boolean isShort() { - return false; - } - - @Override - public boolean isInt() { - return false; - } - - @Override - public boolean isLong() { - return false; - } - - @Override - public boolean isFloat() { - return false; - } - - @Override - public boolean isDouble() { - return false; - } - - @Override - public boolean isGeneric() { - return false; - } - - @Override - public List getTypeArguments() { - return null; - } - - @Override - public boolean isTypeVar() { - return false; - } - - @Override - public boolean isWildcard() { - return false; - } - - @Override - public boolean isInterface() { - return false; - } - - @Override - public boolean isVoid() { - return false; - } - - @Override - public ModelClass unbox() { - return this; - } - - @Override - public ModelClass box() { - return this; - } - - @Override - public boolean isObservable() { - return getSuperclass().isObservable(); - } - - @Override - public boolean isAssignableFrom(ModelClass that) { - ModelClass superClass = that; - while (superClass != null && !superClass.isObject()) { - if (superClass.toJavaCode().equals(mClassName)) { - return true; - } - } - return false; - } - - @Override - public ModelClass getSuperclass() { - return ModelAnalyzer.getInstance().findClass(mSuperClass, null); - } - - @Override - public ModelClass erasure() { - return this; - } - - @Override - public String getJniDescription() { - return TypeUtil.getInstance().getDescription(this); - } - - @Override - protected ModelField[] getDeclaredFields() { - ModelClass superClass = getSuperclass(); - final ModelField[] superFields = superClass.getDeclaredFields(); - final int fieldCount = superFields.length + mFields.size(); - final ModelField[] fields = Arrays.copyOf(superFields, fieldCount); - int index = superFields.length; - for (String fieldName : mFields.keySet()) { - final String fieldType = mFields.get(fieldName); - fields[index++] = new InjectedBindingClassField(fieldName, fieldType); - } - return fields; - } - - @Override - protected ModelMethod[] getDeclaredMethods() { - ModelClass superClass = getSuperclass(); - final ModelMethod[] superMethods = superClass.getDeclaredMethods(); - final int methodCount = superMethods.length + (mVariables.size() * 2); - final ModelMethod[] methods = Arrays.copyOf(superMethods, methodCount); - int index = superMethods.length; - for (String variableName : mVariables.keySet()) { - final String variableType = mVariables.get(variableName); - final String getterName = "get" + StringUtils.capitalize(variableName); - methods[index++] = new InjectedBindingClassMethod(this, getterName, variableType, null); - final String setterName = "set" + StringUtils.capitalize(variableName); - methods[index++] = new InjectedBindingClassMethod(this, setterName, "void", variableType); - } - return methods; - } - - @Override - public String toString() { - return "Injected Class: " + mClassName; - } -} diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassField.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassField.java deleted file mode 100644 index d15cc908..00000000 --- a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassField.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2016 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.reflection; - -import java.util.Map; - -/** - * A class that can be used by ModelAnalyzer without any backing model. This is used - * for fields on ViewDataBinding subclasses that haven't been generated yet. - * - * @see ModelAnalyzer#injectViewDataBinding(String, Map, Map) - */ -public class InjectedBindingClassField extends ModelField { - private final String mType; - private final String mName; - - public InjectedBindingClassField(String name, String type) { - mName = name; - mType = type; - } - - @Override - public boolean isBindable() { - return false; - } - - @Override - public String getName() { - return mName; - } - - @Override - public boolean isPublic() { - return true; - } - - @Override - public boolean isStatic() { - return false; - } - - @Override - public boolean isFinal() { - return true; - } - - @Override - public ModelClass getFieldType() { - return ModelAnalyzer.getInstance().findClass(mType, null); - } -} diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassMethod.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassMethod.java deleted file mode 100644 index 20e24824..00000000 --- a/compiler/src/main/java/android/databinding/tool/reflection/InjectedBindingClassMethod.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2016 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.reflection; - -import java.util.List; -import java.util.Map; - -/** - * A class that can be used by ModelAnalyzer without any backing model. This is used - * for methods on ViewDataBinding subclasses that haven't been generated yet. - * - * @see ModelAnalyzer#injectViewDataBinding(String, Map, Map) - */ -public class InjectedBindingClassMethod extends ModelMethod { - private final InjectedBindingClass mContainingClass; - private final String mName; - private final String mReturnType; - private final String mParameter; - - public InjectedBindingClassMethod(InjectedBindingClass containingClass, String name, String returnType, - String parameter) { - mContainingClass = containingClass; - mName = name; - mReturnType = returnType; - mParameter = parameter; - } - - @Override - public ModelClass getDeclaringClass() { - return mContainingClass; - } - - @Override - public ModelClass[] getParameterTypes() { - if (mParameter != null) { - ModelClass parameterType = ModelAnalyzer.getInstance().findClass(mParameter, null); - return new ModelClass[] { parameterType }; - } - return new ModelClass[0]; - } - - @Override - public String getName() { - return mName; - } - - @Override - public ModelClass getReturnType(List args) { - ModelClass returnType = ModelAnalyzer.getInstance().findClass(mReturnType, null); - return returnType; - } - - @Override - public boolean isVoid() { - return getReturnType().isVoid(); - } - - @Override - public boolean isPublic() { - return true; - } - - @Override - public boolean isProtected() { - return false; - } - - @Override - public boolean isStatic() { - return false; - } - - @Override - public boolean isAbstract() { - return true; - } - - @Override - public boolean isBindable() { - return false; - } - - @Override - public int getMinApi() { - return 0; - } - - @Override - public String getJniDescription() { - return TypeUtil.getInstance().getDescription(this); - } - - @Override - public boolean isVarArgs() { - return false; - } -} diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java new file mode 100644 index 00000000..45857991 --- /dev/null +++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2016 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.reflection; + +import android.databinding.tool.util.StringUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * A class that can be used by ModelAnalyzer without any backing model. This is used + * for ViewDataBinding subclasses that haven't been generated yet, but we still want + * to resolve methods and fields for them. + * + * @see ModelAnalyzer#injectViewDataBinding(String, Map, Map) + */ +public class InjectedClass extends ModelClass { + private final String mClassName; + private final String mSuperClass; + private final List mMethods = new ArrayList(); + private final List mFields = new ArrayList(); + + public InjectedClass(String className, String superClass) { + mClassName = className; + mSuperClass = superClass; + } + + public void addField(InjectedField field) { + mFields.add(field); + } + + public void addMethod(InjectedMethod method) { + mMethods.add(method); + } + + @Override + public String toJavaCode() { + return mClassName; + } + + @Override + public boolean isArray() { + return false; + } + + @Override + public ModelClass getComponentType() { + return null; + } + + @Override + public boolean isNullable() { + return true; + } + + @Override + public boolean isPrimitive() { + return false; + } + + @Override + public boolean isBoolean() { + return false; + } + + @Override + public boolean isChar() { + return false; + } + + @Override + public boolean isByte() { + return false; + } + + @Override + public boolean isShort() { + return false; + } + + @Override + public boolean isInt() { + return false; + } + + @Override + public boolean isLong() { + return false; + } + + @Override + public boolean isFloat() { + return false; + } + + @Override + public boolean isDouble() { + return false; + } + + @Override + public boolean isGeneric() { + return false; + } + + @Override + public List getTypeArguments() { + return null; + } + + @Override + public boolean isTypeVar() { + return false; + } + + @Override + public boolean isWildcard() { + return false; + } + + @Override + public boolean isInterface() { + return false; + } + + @Override + public boolean isVoid() { + return false; + } + + @Override + public ModelClass unbox() { + return this; + } + + @Override + public ModelClass box() { + return this; + } + + @Override + public boolean isObservable() { + return getSuperclass().isObservable(); + } + + @Override + public boolean isAssignableFrom(ModelClass that) { + ModelClass superClass = that; + while (superClass != null && !superClass.isObject()) { + if (superClass.toJavaCode().equals(mClassName)) { + return true; + } + } + return false; + } + + @Override + public ModelClass getSuperclass() { + return ModelAnalyzer.getInstance().findClass(mSuperClass, null); + } + + @Override + public ModelClass erasure() { + return this; + } + + @Override + public String getJniDescription() { + return TypeUtil.getInstance().getDescription(this); + } + + @Override + protected ModelField[] getDeclaredFields() { + ModelClass superClass = getSuperclass(); + final ModelField[] superFields = superClass.getDeclaredFields(); + final int initialCount = superFields.length; + final int fieldCount = initialCount + mFields.size(); + final ModelField[] fields = Arrays.copyOf(superFields, fieldCount); + for (int i = 0; i < mFields.size(); i++) { + fields[i + initialCount] = mFields.get(i); + } + return fields; + } + + @Override + protected ModelMethod[] getDeclaredMethods() { + ModelClass superClass = getSuperclass(); + final ModelMethod[] superMethods = superClass.getDeclaredMethods(); + final int initialCount = superMethods.length; + final int methodCount = initialCount + mMethods.size(); + final ModelMethod[] methods = Arrays.copyOf(superMethods, methodCount); + for (int i = 0; i < mMethods.size(); i++) { + methods[i + initialCount] = mMethods.get(i); + } + return methods; + } + + @Override + public String toString() { + return "Injected Class: " + mClassName; + } +} diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedField.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedField.java new file mode 100644 index 00000000..85719f1c --- /dev/null +++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedField.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 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.reflection; + +import java.util.Map; + +/** + * A class that can be used by ModelAnalyzer without any backing model. This is used + * for fields on ViewDataBinding subclasses that haven't been generated yet. + * + * @see ModelAnalyzer#injectViewDataBinding(String, Map, Map) + */ +public class InjectedField extends ModelField { + private final String mType; + private final String mName; + + public InjectedField(String name, String type) { + mName = name; + mType = type; + } + + @Override + public boolean isBindable() { + return false; + } + + @Override + public String getName() { + return mName; + } + + @Override + public boolean isPublic() { + return true; + } + + @Override + public boolean isStatic() { + return false; + } + + @Override + public boolean isFinal() { + return true; + } + + @Override + public ModelClass getFieldType() { + return ModelAnalyzer.getInstance().findClass(mType, null); + } +} diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java new file mode 100644 index 00000000..f47442b7 --- /dev/null +++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 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.reflection; + +import java.util.List; +import java.util.Map; + +/** + * A class that can be used by ModelAnalyzer without any backing model. This is used + * for methods on ViewDataBinding subclasses that haven't been generated yet. + * + * @see ModelAnalyzer#injectViewDataBinding(String, Map, Map) + */ +public class InjectedMethod extends ModelMethod { + private final InjectedClass mContainingClass; + private final String mName; + private final String mReturnTypeName; + private final String[] mParameterTypeNames; + private ModelClass[] mParameterTypes; + private ModelClass mReturnType; + private boolean mIsStatic; + + public InjectedMethod(InjectedClass containingClass, boolean isStatic, String name, + String returnType, String... parameters) { + mContainingClass = containingClass; + mName = name; + mIsStatic = isStatic; + mReturnTypeName = returnType; + mParameterTypeNames = parameters; + } + + @Override + public ModelClass getDeclaringClass() { + return mContainingClass; + } + + @Override + public ModelClass[] getParameterTypes() { + if (mParameterTypes == null) { + if (mParameterTypeNames == null) { + mParameterTypes = new ModelClass[0]; + } else { + mParameterTypes = new ModelClass[mParameterTypeNames.length]; + ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); + for (int i = 0; i < mParameterTypeNames.length; i++) { + mParameterTypes[i] = modelAnalyzer.findClass(mParameterTypeNames[i], null); + } + } + } + return mParameterTypes; + } + + @Override + public String getName() { + return mName; + } + + @Override + public ModelClass getReturnType(List args) { + if (mReturnType == null) { + mReturnType = ModelAnalyzer.getInstance().findClass(mReturnTypeName, null); + } + return mReturnType; + } + + @Override + public boolean isVoid() { + return getReturnType().isVoid(); + } + + @Override + public boolean isPublic() { + return true; + } + + @Override + public boolean isProtected() { + return false; + } + + @Override + public boolean isStatic() { + return mIsStatic; + } + + @Override + public boolean isAbstract() { + return true; + } + + @Override + public boolean isBindable() { + return false; + } + + @Override + public int getMinApi() { + return 0; + } + + @Override + public String getJniDescription() { + return TypeUtil.getInstance().getDescription(this); + } + + @Override + public boolean isVarArgs() { + return false; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("public "); + if (mIsStatic) { + sb.append("static "); + } + sb.append(mReturnTypeName) + .append(' ') + .append(mName) + .append("("); + if (mParameterTypeNames != null) { + for (int i = 0; i < mParameterTypeNames.length; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append(mParameterTypeNames[i]); + } + } + sb.append(')'); + return sb.toString(); + } +} diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java index 8943200b..995ae219 100644 --- a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java +++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java @@ -18,6 +18,7 @@ package android.databinding.tool.reflection; import android.databinding.tool.reflection.annotation.AnnotationAnalyzer; import android.databinding.tool.util.L; import android.databinding.tool.util.Preconditions; +import android.databinding.tool.util.StringUtils; import java.util.HashMap; import java.util.Map; @@ -83,8 +84,8 @@ public abstract class ModelAnalyzer { private ModelClass mViewStubType; private static ModelAnalyzer sAnalyzer; - private final Map mInjectedClasses = - new HashMap(); + private final Map mInjectedClasses = + new HashMap(); protected void setInstance(ModelAnalyzer analyzer) { sAnalyzer = analyzer; @@ -234,10 +235,33 @@ public abstract class ModelAnalyzer { public abstract TypeUtil createTypeUtil(); + public ModelClass injectClass(InjectedClass injectedClass) { + mInjectedClasses.put(injectedClass.getCanonicalName(), injectedClass); + return injectedClass; + } + public ModelClass injectViewDataBinding(String className, Map variables, Map fields) { - InjectedBindingClass injectedClass = new InjectedBindingClass(className, - ModelAnalyzer.VIEW_DATA_BINDING, variables, fields); + InjectedClass injectedClass = new InjectedClass(className, + ModelAnalyzer.VIEW_DATA_BINDING); + + if (fields != null) { + for (String name : fields.keySet()) { + String type = fields.get(name); + injectedClass.addField(new InjectedField(name, type)); + } + } + if (variables != null) { + for (String name : variables.keySet()) { + String type = variables.get(name); + String capName = StringUtils.capitalize(name); + String setName = "set" + capName; + String getName = "get" + capName; + injectedClass.addMethod(new InjectedMethod(injectedClass, false, getName, type)); + injectedClass.addMethod(new InjectedMethod(injectedClass, false, setName, "void", + type)); + } + } mInjectedClasses.put(className, injectedClass); return injectedClass; } diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java b/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java index 5bd214e3..3ec7ff8d 100644 --- a/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java +++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelMethod.java @@ -96,6 +96,9 @@ public abstract class ModelMethod { final ModelClass arg = args.get(i); final ModelClass thisParameter = getParameter(i, parameterTypes); final ModelClass thatParameter = other.getParameter(i, otherParameterTypes); + if (thisParameter.equals(thatParameter)) { + continue; + } final int diff = compareParameter(arg, thisParameter, thatParameter); if (diff != 0) { return diff < 0; -- cgit v1.2.3